Skip to content

File xasl_generation.c

File List > cubrid > src > parser > xasl_generation.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.
 *
 */

/*
 * xasl_generation.c - Generate XASL from the parse tree
 */

#ident "$Id$"

#include "config.h"

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

#include "xasl_generation.h"

#include "authenticate.h"
#include "misc_string.h"
#include "error_manager.h"
#include "parser.h"
#include "xasl_aggregate.hpp"
#include "xasl_analytic.hpp"
#include "xasl_predicate.hpp"
#include "xasl_regu_alloc.hpp"
#include "db.h"
#include "environment_variable.h"
#include "parser.h"
#include "schema_manager.h"
#include "view_transform.h"
#include "locator_cl.h"
#include "optimizer.h"
#include "parser_message.h"
#include "virtual_object.h"
#include "set_object.h"
#include "object_primitive.h"
#include "object_print.h"
#include "object_representation.h"
#include "intl_support.h"
#include "system_parameter.h"
#include "execute_schema.h"
#include "porting.h"
#include "execute_statement.h"
#include "query_graph.h"
#include "schema_system_catalog_constants.h"
#include "query_planner.h"
#include "semantic_check.h"
#include "query_dump.h"
#include "parser_support.h"
#include "compile_context.h"
#include "db_json.hpp"
#include "jsp_cl.h"
#include "subquery_cache.h"
#include "pl_signature.hpp"
#include "sp_catalog.hpp"
#include "px_heap_scan_checker.hpp"
#include "px_query_checker.hpp"
#if defined(WINDOWS)
#include "wintcp.h"
#endif /* WINDOWS */

#include "dbtype.h"

extern void qo_plan_lite_print (QO_PLAN * plan, FILE * f, int howfar);

/* maximum number of unique columns that can be optimized */
#define ANALYTIC_OPT_MAX_SORT_LIST_COLUMNS      32

/* maximum number of functions that can be optimized */
#define ANALYTIC_OPT_MAX_FUNCTIONS              32

typedef struct hashable HASHABLE;
struct hashable
{
  bool is_PRIOR;
  bool is_NAME_without_prior;
};

typedef enum
{
  UNHASHABLE = 1,
  PROBE,
  BUILD,
  CONSTANT
} HASH_ATTR;

#define CHECK_HASH_ATTR(hashable_arg, hash_attr) \
  do \
    { \
      if (hashable_arg.is_PRIOR && hashable_arg.is_NAME_without_prior) \
        { \
          hash_attr = UNHASHABLE; \
        } \
      else if (hashable_arg.is_PRIOR && !hashable_arg.is_NAME_without_prior) \
        { \
          hash_attr = PROBE; \
        } \
      else if (!hashable_arg.is_PRIOR && hashable_arg.is_NAME_without_prior) \
        { \
          hash_attr = BUILD; \
        } \
      else \
        { \
          hash_attr = CONSTANT; \
        } \
    } \
  while (0)

typedef struct analytic_key_metadomain ANALYTIC_KEY_METADOMAIN;
struct analytic_key_metadomain
{
  /* indexed sort list */
  unsigned char key[ANALYTIC_OPT_MAX_FUNCTIONS];

  /* sort list size */
  int key_size;

  /* partition prefix size */
  int part_size;

  /* compatibility links */
  ANALYTIC_KEY_METADOMAIN *links[ANALYTIC_OPT_MAX_FUNCTIONS];
  int links_count;

  /* if composite metadomain then the two children, otherwise null */
  ANALYTIC_KEY_METADOMAIN *children[2];

  /* true if metadomain is now part of composite metadomain */
  bool demoted;

  /* level of metadomain */
  int level;

  /* source function */
  ANALYTIC_TYPE *source;
};

/* metadomain initializer */
static ANALYTIC_KEY_METADOMAIN analitic_key_metadomain_Initializer = { {0}, 0, 0, {NULL}, 0, {NULL}, false, 0, NULL };

typedef enum
{
  SORT_LIST_AFTER_ISCAN = 1,
  SORT_LIST_ORDERBY,
  SORT_LIST_GROUPBY,
  SORT_LIST_AFTER_GROUPBY,
  SORT_LIST_ANALYTIC_WINDOW
} SORT_LIST_MODE;

typedef struct set_numbering_node_etc_info
{
  DB_VALUE **instnum_valp;
  DB_VALUE **ordbynum_valp;
} SET_NUMBERING_NODE_ETC_INFO;

typedef struct pred_regu_variable_p_list_node *PRED_REGU_VARIABLE_P_LIST, PRED_REGU_VARIABLE_P_LIST_NODE;
struct pred_regu_variable_p_list_node
{
  PRED_REGU_VARIABLE_P_LIST next;   /* next node */
  const REGU_VARIABLE *pvalue;  /* pointer to regulator variable */
  bool is_prior;        /* is it in PRIOR argument? */
};

#define SORT_SPEC_EQ(a, b) \
  ((a)->info.sort_spec.pos_descr.pos_no == (b)->info.sort_spec.pos_descr.pos_no \
   && (a)->info.sort_spec.asc_or_desc == (b)->info.sort_spec.asc_or_desc \
   && (a)->info.sort_spec.nulls_first_or_last == (b)->info.sort_spec.nulls_first_or_last)

static PRED_EXPR *pt_make_pred_term_not (const PRED_EXPR * arg1);
static PRED_EXPR *pt_make_pred_term_comp (const REGU_VARIABLE * arg1, const REGU_VARIABLE * arg2, const REL_OP rop,
                      const DB_TYPE data_type);
static PRED_EXPR *pt_make_pred_term_some_all (const REGU_VARIABLE * arg1, const REGU_VARIABLE * arg2, const REL_OP rop,
                          const DB_TYPE data_type, const QL_FLAG some_all);
static PRED_EXPR *pt_make_pred_term_like (const REGU_VARIABLE * arg1, const REGU_VARIABLE * arg2,
                      const REGU_VARIABLE * arg3);
static PRED_EXPR *pt_make_pred_term_rlike (REGU_VARIABLE * arg1, REGU_VARIABLE * arg2, REGU_VARIABLE * case_sensitive);
static PRED_EXPR *pt_make_pred_term_is (PARSER_CONTEXT * parser, PT_NODE * arg1, PT_NODE * arg2, const BOOL_OP bop);
static PRED_EXPR *pt_to_pred_expr_local_with_arg (PARSER_CONTEXT * parser, PT_NODE * node, int *argp);

#if defined (ENABLE_UNUSED_FUNCTION)
static int hhhhmmss (const DB_TIME * time, char *buf, int buflen);
static int hhmiss (const DB_TIME * time, char *buf, int buflen);
static int yyyymmdd (const DB_DATE * date, char *buf, int buflen);
static int yymmdd (const DB_DATE * date, char *buf, int buflen);
static int yymmddhhmiss (const DB_UTIME * utime, char *buf, int buflen);
static int mmddyyyyhhmiss (const DB_UTIME * utime, char *buf, int buflen);
static int yyyymmddhhmissms (const DB_DATETIME * datetime, char *buf, int buflen);
static int mmddyyyyhhmissms (const DB_DATETIME * datetime, char *buf, int buflen);

static char *host_var_name (unsigned int custom_print);
#endif
static PT_NODE *pt_table_compatible_node (PARSER_CONTEXT * parser, PT_NODE * tree, void *void_info, int *continue_walk);
static int pt_table_compatible (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * spec);
static TABLE_INFO *pt_table_info_alloc (void);
static PT_NODE *pt_filter_pseudo_specs (PARSER_CONTEXT * parser, PT_NODE * spec);
static PT_NODE *pt_is_hash_agg_eligible (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk);
static PT_NODE *pt_to_aggregate_node (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk);
static PT_NODE *pt_to_analytic_node (PARSER_CONTEXT * parser, PT_NODE * tree, ANALYTIC_INFO * analytic_info);
static PT_NODE *pt_to_analytic_final_node (PARSER_CONTEXT * parser, PT_NODE * tree, PT_NODE ** ex_list,
                       int *instnum_flag);
static PT_NODE *pt_expand_analytic_node (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * select_list);
static PT_NODE *pt_set_analytic_node_etc (PARSER_CONTEXT * parser, PT_NODE * node);
static void pt_adjust_analytic_sort_specs (PARSER_CONTEXT * parser, PT_NODE * node, int idx, int adjust);
static PT_NODE *pt_resolve_analytic_references (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * select_list,
                        VAL_LIST * vallist);
static PT_NODE *pt_substitute_analytic_references (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE ** ex_list);
static SYMBOL_INFO *pt_push_fetch_spec_info (PARSER_CONTEXT * parser, SYMBOL_INFO * symbols, PT_NODE * fetch_spec);
static ACCESS_SPEC_TYPE *pt_make_access_spec (TARGET_TYPE spec_type, ACCESS_METHOD access, INDX_INFO * indexptr,
                          PRED_EXPR * where_key, PRED_EXPR * where_pred, PRED_EXPR * where_range);
static int pt_cnt_attrs (const REGU_VARIABLE_LIST attr_list);
static void pt_fill_in_attrid_array (REGU_VARIABLE_LIST attr_list, ATTR_ID * attr_array, int *next_pos);
static SORT_LIST *pt_to_sort_list (PARSER_CONTEXT * parser, PT_NODE * node_list, PT_NODE * col_list,
                   SORT_LIST_MODE sort_mode);

static int regu_make_constant_vid (DB_VALUE * val, DB_VALUE ** dbvalptr);
static int set_has_objs (DB_SET * seq);
static int setof_mop_to_setof_vobj (PARSER_CONTEXT * parser, DB_SET * seq, DB_VALUE * new_val);
static REGU_VARIABLE *pt_make_regu_hostvar (PARSER_CONTEXT * parser, const PT_NODE * node);
static REGU_VARIABLE *pt_make_regu_reguvalues_list (PARSER_CONTEXT * parser, const PT_NODE * node, UNBOX unbox);
static REGU_VARIABLE *pt_make_regu_constant (PARSER_CONTEXT * parser, DB_VALUE * db_value, const DB_TYPE db_type,
                         const PT_NODE * node);
static REGU_VARIABLE *pt_make_regu_pred (const PRED_EXPR * pred);
static REGU_VARIABLE *pt_make_function (PARSER_CONTEXT * parser, int function_code, const REGU_VARIABLE_LIST arg_list,
                    const DB_TYPE result_type, const PT_NODE * node);

static REGU_VARIABLE *pt_stored_procedure_to_regu (PARSER_CONTEXT * parser, PT_NODE * node);
static REGU_VARIABLE *pt_function_to_regu (PARSER_CONTEXT * parser, PT_NODE * function);
static REGU_VARIABLE *pt_make_regu_subquery (PARSER_CONTEXT * parser, XASL_NODE * xasl, const UNBOX unbox,
                         const PT_NODE * node);
static REGU_VARIABLE *pt_make_regu_insert (PARSER_CONTEXT * parser, PT_NODE * statement);
static PT_NODE *pt_set_numbering_node_etc_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static REGU_VARIABLE *pt_make_regu_numbering (PARSER_CONTEXT * parser, const PT_NODE * node);
static void pt_to_misc_operand (REGU_VARIABLE * regu, PT_MISC_TYPE misc_specifier);
static REGU_VARIABLE *pt_make_position_regu_variable (PARSER_CONTEXT * parser, const PT_NODE * node, int i);
static REGU_VARIABLE *pt_to_regu_attr_descr (PARSER_CONTEXT * parser, DB_OBJECT * class_object,
                         HEAP_CACHE_ATTRINFO * cache_attrinfo, PT_NODE * attr);
static REGU_VARIABLE *pt_make_vid (PARSER_CONTEXT * parser, const PT_NODE * data_type, const REGU_VARIABLE * regu3);
static PT_NODE *pt_make_prefix_index_data_filter (PARSER_CONTEXT * parser, PT_NODE * where_key_part,
                          PT_NODE * where_part, QO_XASL_INDEX_INFO * index_pred);
static REGU_VARIABLE *pt_make_pos_regu_var_from_scratch (TP_DOMAIN * dom, DB_VALUE * fetch_to, int pos_no);
static PT_NODE *pt_set_level_node_etc_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static REGU_VARIABLE *pt_make_regu_level (PARSER_CONTEXT * parser, const PT_NODE * node);
static PT_NODE *pt_set_isleaf_node_etc_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static REGU_VARIABLE *pt_make_regu_isleaf (PARSER_CONTEXT * parser, const PT_NODE * node);
static PT_NODE *pt_set_iscycle_node_etc_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static REGU_VARIABLE *pt_make_regu_iscycle (PARSER_CONTEXT * parser, const PT_NODE * node);
static PT_NODE *pt_set_connect_by_operator_node_etc_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg,
                             int *continue_walk);
static PT_NODE *pt_set_qprior_node_etc_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static void pt_fix_pseudocolumns_pos_regu_list (PARSER_CONTEXT * parser, PT_NODE * node_list,
                        REGU_VARIABLE_LIST regu_list);
static XASL_NODE *pt_find_oid_scan_block (XASL_NODE * xasl, OID * oid);
static PT_NODE *pt_numbering_set_continue_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static int pt_fix_first_term_expr_for_iss (PARSER_CONTEXT * parser, QO_INDEX_ENTRY * index_entryp,
                       PT_NODE ** term_exprs);
static int pt_fix_first_term_func_index_for_iss (PARSER_CONTEXT * parser, QO_INDEX_ENTRY * index_entryp,
                         PT_NODE ** term_exprs);
static int pt_create_iss_range (INDX_INFO * indx_infop, TP_DOMAIN * domain);
static int pt_init_pred_expr_context (PARSER_CONTEXT * parser, PT_NODE * predicate, PT_NODE * spec,
                      PRED_EXPR_WITH_CONTEXT * pred_expr);
static bool validate_regu_key_function_index (REGU_VARIABLE * regu_var);
static XASL_NODE *pt_to_merge_update_xasl (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE ** non_null_attrs);
static XASL_NODE *pt_to_merge_insert_xasl (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * non_null_attrs,
                       PT_NODE * default_expr_attrs);
static PT_NODE *pt_append_assignment_references (PARSER_CONTEXT * parser, PT_NODE * assignments, PT_NODE * from,
                         PT_NODE * select_list);
static ODKU_INFO *pt_to_odku_info (PARSER_CONTEXT * parser, PT_NODE * insert, XASL_NODE * xasl);
static REGU_VARIABLE *pt_to_cume_dist_percent_rank_regu_variable (PARSER_CONTEXT * parser, PT_NODE * tree, UNBOX unbox);

static REGU_VARIABLE *pt_to_regu_reserved_name (PARSER_CONTEXT * parser, PT_NODE * attr);
static int pt_reserved_id_to_valuelist_index (PARSER_CONTEXT * parser, PT_RESERVED_NAME_ID reserved_id);
static void pt_mark_spec_list_for_update_clause (PARSER_CONTEXT * parser, PT_NODE * statement, PT_SPEC_FLAG spec_flag);

static void pt_optimize_min_max_list (PARSER_CONTEXT * parser, PT_NODE * select_node, QO_PLAN * plan,
                      AGGREGATE_TYPE * aggregate);
static void pt_aggregate_info_append_value_list (AGGREGATE_INFO * info, VAL_LIST * value_list);

static void pt_aggregate_info_update_value_and_reguvar_lists (AGGREGATE_INFO * info, VAL_LIST * value_list,
                                  REGU_VARIABLE_LIST regu_position_list,
                                  REGU_VARIABLE_LIST regu_constant_list);

static void pt_aggregate_info_update_scan_regu_list (AGGREGATE_INFO * info, REGU_VARIABLE_LIST scan_regu_list);

static PT_NODE *pt_node_list_to_value_and_reguvar_list (PARSER_CONTEXT * parser, PT_NODE * node, VAL_LIST ** value_list,
                            REGU_VARIABLE_LIST * regu_position_list);

static PT_NODE *pt_make_regu_list_from_value_list (PARSER_CONTEXT * parser, PT_NODE * node, VAL_LIST * value_list,
                           REGU_VARIABLE_LIST * regu_list);

static int pt_make_constant_regu_list_from_val_list (PARSER_CONTEXT * parser, VAL_LIST * value_list,
                             REGU_VARIABLE_LIST * regu_list);

/* *INDENT-OFF* */
static void pt_set_regu_list_pos_descr_from_idx (REGU_VARIABLE_LIST & regu_list, size_t starting_index);
/* *INDENT-ON* */

static PT_NODE *pt_fix_interpolation_aggregate_function_order_by (PARSER_CONTEXT * parser, PT_NODE * node);
static int pt_fix_buildlist_aggregate_cume_dist_percent_rank (PARSER_CONTEXT * parser, PT_NODE * node,
                                  AGGREGATE_INFO * info, REGU_VARIABLE * regu);

static PT_NODE *pt_check_dblink_trigger_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static void pt_check_dblink_trigger (PARSER_CONTEXT * parser, PT_NODE * statement);

#define APPEND_TO_XASL(xasl_head, list, xasl_tail) \
  do \
    { \
      if (xasl_head) \
        { \
          /* append xasl_tail to end of linked list denoted by list */ \
          XASL_NODE **NAME2(list, ptr) = &xasl_head->list; \
          while ((*NAME2(list, ptr))) \
            { \
              NAME2(list, ptr) = &(*NAME2(list, ptr))->list; \
            } \
          (*NAME2(list, ptr)) = xasl_tail; \
        } \
      else \
        { \
          xasl_head = xasl_tail; \
        } \
    } \
  while (0)

#define VALIDATE_REGU_KEY_HELPER(r) \
  ((r)->type == TYPE_CONSTANT || (r)->type == TYPE_DBVAL || (r)->type == TYPE_POS_VALUE || (r)->type == TYPE_INARITH)

#define VALIDATE_REGU_KEY(r) \
  ((r)->type == TYPE_CONSTANT || (r)->type == TYPE_DBVAL || (r)->type == TYPE_POS_VALUE || (r)->type == TYPE_SP \
   || ((r)->type == TYPE_INARITH && validate_regu_key_function_index ((r))))

typedef struct xasl_supp_info
{
  PT_NODE *query_list;      /* ??? */

  /* XASL cache related information */
  OID *class_oid_list;      /* list of class/serial OIDs referenced in the XASL */
  int *class_locks;     /* list of locks required for each class in class_oid_list. */
  int *tcard_list;      /* list of #pages of the class OIDs */
  int n_oid_list;       /* number OIDs in the list */
  int oid_list_size;        /* size of the list */
  int includes_tde_class;   /* whether there are some tde class in class_oid_list: 0 or 1 */
} XASL_SUPP_INFO;

typedef struct uncorr_info
{
  XASL_NODE *xasl;
  int level;
} UNCORR_INFO;

typedef struct corr_info
{
  XASL_NODE *xasl_head;
  UINTPTR id;
} CORR_INFO;

FILE *query_Plan_dump_fp = NULL;
char *query_Plan_dump_filename = NULL;
bool query_Plan_dump_fp_open = false;

static XASL_SUPP_INFO xasl_Supp_info = { NULL, NULL, NULL, NULL, 0, 0, 0 };

static const int OID_LIST_GROWTH = 10;


static RANGE op_type_to_range (const PT_OP_TYPE op_type, const int nterms);
static int pt_to_single_key (PARSER_CONTEXT * parser, PT_NODE ** term_exprs, int nterms, bool multi_col,
                 KEY_INFO * key_infop, int *multi_col_pos);
static int pt_to_range_key (PARSER_CONTEXT * parser, PT_NODE ** term_exprs, int nterms, bool multi_col,
                KEY_INFO * key_infop);
static int pt_to_list_key (PARSER_CONTEXT * parser, PT_NODE ** term_exprs, int nterms, bool multi_col,
               KEY_INFO * key_infop);
static int pt_to_rangelist_key (PARSER_CONTEXT * parser, PT_NODE ** term_exprs, int nterms, bool multi_col,
                KEY_INFO * key_infop, int rangelist_idx, int *multi_col_pos);
static int pt_to_key_limit (PARSER_CONTEXT * parser, PT_NODE * key_limit, QO_LIMIT_INFO * limit_infop,
                KEY_INFO * key_infop, bool key_limit_reset);
static int pt_instnum_to_key_limit (PARSER_CONTEXT * parser, QO_PLAN * plan, XASL_NODE * xasl);
static int pt_ordbynum_to_key_limit_multiple_ranges (PARSER_CONTEXT * parser, QO_PLAN * plan, XASL_NODE * xasl);
static INDX_INFO *pt_to_index_info (PARSER_CONTEXT * parser, DB_OBJECT * class_, PRED_EXPR * where_pred, QO_PLAN * plan,
                    QO_XASL_INDEX_INFO * qo_index_infop);
static ACCESS_SPEC_TYPE *pt_to_class_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * where_key_part,
                        PT_NODE * where_part, QO_PLAN * plan, QO_XASL_INDEX_INFO * index_pred);
static ACCESS_SPEC_TYPE *pt_to_subquery_table_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * subquery,
                             PT_NODE * where_part, PT_NODE * where_hash_part);
static ACCESS_SPEC_TYPE *pt_to_showstmt_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * where_part);
static ACCESS_SPEC_TYPE *pt_to_set_expr_table_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * set_expr,
                             PT_NODE * where_part);
static ACCESS_SPEC_TYPE *pt_to_cselect_table_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * cselect,
                            PT_NODE * src_derived_tbl);
static ACCESS_SPEC_TYPE *pt_to_json_table_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * json_table,
                             PT_NODE * src_derived_tbl, PT_NODE * where_p);
static ACCESS_SPEC_TYPE *pt_to_dblink_table_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec,
                               PT_NODE * dblink_table, PT_NODE * src_derived_tbl,
                               PT_NODE * where_p);

static ACCESS_SPEC_TYPE *pt_make_json_table_access_spec (PARSER_CONTEXT * parser, REGU_VARIABLE * json_reguvar,
                             PRED_EXPR * where_pred, PT_JSON_TABLE_INFO * json_table,
                             TABLE_INFO * tbl_info);
static json_table_node *pt_make_json_table_spec_node (PARSER_CONTEXT * parser, PT_JSON_TABLE_INFO * json_table,
                              size_t & start_id, TABLE_INFO * tbl_info);
static void pt_make_json_table_spec_node_internal (PARSER_CONTEXT * parser, PT_JSON_TABLE_NODE_INFO * jt_node_info,
                           size_t & current_id, TABLE_INFO * tbl_info,
                           json_table_node & result);
static XASL_NODE *pt_find_xasl (XASL_NODE * list, XASL_NODE * match);
static void pt_set_aptr (PARSER_CONTEXT * parser, PT_NODE * select_node, XASL_NODE * xasl);
static XASL_NODE *pt_append_scan (const XASL_NODE * to, const XASL_NODE * from);
static PT_NODE *pt_uncorr_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_uncorr_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static XASL_NODE *pt_to_uncorr_subquery_list (PARSER_CONTEXT * parser, PT_NODE * node);
static PT_NODE *pt_corr_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static XASL_NODE *pt_to_corr_subquery_list (PARSER_CONTEXT * parser, PT_NODE * node, UINTPTR id);
static SELUPD_LIST *pt_link_regu_to_selupd_list (PARSER_CONTEXT * parser, REGU_VARIABLE_LIST regulist,
                         SELUPD_LIST * selupd_list, DB_OBJECT * target_class);
static OUTPTR_LIST *pt_to_outlist (PARSER_CONTEXT * parser, PT_NODE * node_list, SELUPD_LIST ** selupd_list_ptr,
                   UNBOX unbox);
static void pt_to_fetch_proc_list_recurse (PARSER_CONTEXT * parser, PT_NODE * spec, XASL_NODE * root);
static void pt_to_fetch_proc_list (PARSER_CONTEXT * parser, PT_NODE * spec, XASL_NODE * root);
static XASL_NODE *pt_to_scan_proc_list (PARSER_CONTEXT * parser, PT_NODE * node, XASL_NODE * root);
static XASL_NODE *pt_gen_optimized_plan (PARSER_CONTEXT * parser, PT_NODE * select_node, QO_PLAN * plan,
                     XASL_NODE * xasl);
static XASL_NODE *pt_gen_simple_plan (PARSER_CONTEXT * parser, PT_NODE * select_node, QO_PLAN * plan, XASL_NODE * xasl);
static XASL_NODE *pt_to_buildlist_proc (PARSER_CONTEXT * parser, PT_NODE * select_node, QO_PLAN * qo_plan);
static XASL_NODE *pt_to_buildschema_proc (PARSER_CONTEXT * parser, PT_NODE * select_node);
static XASL_NODE *pt_to_buildvalue_proc (PARSER_CONTEXT * parser, PT_NODE * select_node, QO_PLAN * qo_plan);
static bool pt_analytic_to_metadomain (ANALYTIC_TYPE * func_p, PT_NODE * sort_list, ANALYTIC_KEY_METADOMAIN * func_meta,
                       PT_NODE ** index, int *index_size);
static bool pt_metadomains_compatible (ANALYTIC_KEY_METADOMAIN * f1, ANALYTIC_KEY_METADOMAIN * f2,
                       ANALYTIC_KEY_METADOMAIN * out, int *lost_link_count, int level);
static void pt_metadomain_build_comp_graph (ANALYTIC_KEY_METADOMAIN * af_meta, int af_count, int level);
static SORT_LIST *pt_sort_list_from_metadomain (PARSER_CONTEXT * parser, ANALYTIC_KEY_METADOMAIN * meta,
                        PT_NODE ** sort_list_index, PT_NODE * select_list);
static void pt_metadomain_adjust_key_prefix (ANALYTIC_KEY_METADOMAIN * meta);
static ANALYTIC_EVAL_TYPE *pt_build_analytic_eval_list (PARSER_CONTEXT * parser, QO_PLAN * qo_plan,
                            ANALYTIC_KEY_METADOMAIN * meta, ANALYTIC_EVAL_TYPE * eval,
                            PT_NODE ** sort_list_index, ANALYTIC_INFO * info);
static XASL_NODE *pt_to_union_proc (PARSER_CONTEXT * parser, PT_NODE * node, PROC_TYPE type);
static XASL_NODE *pt_plan_set_query (PARSER_CONTEXT * parser, PT_NODE * node, PROC_TYPE proc_type);
static XASL_NODE *pt_plan_query (PARSER_CONTEXT * parser, PT_NODE * select_node);
static XASL_NODE *pt_plan_schema (PARSER_CONTEXT * parser, PT_NODE * select_node);
static XASL_NODE *parser_generate_xasl_proc (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * query_list);
static bool pt_xasl_spec_has_dblink (XASL_NODE * xasl);
static PT_NODE *parser_generate_xasl_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static int pt_spec_to_xasl_class_oid_list (PARSER_CONTEXT * parser, const PT_NODE * spec, OID ** oid_listp,
                       int **lock_listp, int **tcard_listp, int *nump, int *sizep,
                       int includes_tde_class);
static int pt_serial_to_xasl_class_oid_list (PARSER_CONTEXT * parser, const PT_NODE * serial, OID ** oid_listp,
                         int **lock_listp, int **tcard_listp, int *nump, int *sizep);
static PT_NODE *parser_generate_xasl_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static XASL_NODE *pt_make_aptr_parent_node (PARSER_CONTEXT * parser, PT_NODE * node, PROC_TYPE type);
static int pt_to_constraint_pred (PARSER_CONTEXT * parser, XASL_NODE * xasl, PT_NODE * spec, PT_NODE * non_null_attrs,
                  PT_NODE * attr_list, int attr_offset);
static XASL_NODE *pt_to_fetch_as_scan_proc (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * join_term,
                        XASL_NODE * xasl_to_scan);

static REGU_VARIABLE_LIST pt_to_regu_variable_list (PARSER_CONTEXT * p, PT_NODE * node, UNBOX unbox,
                            VAL_LIST * value_list, int *attr_offsets);

static REGU_VARIABLE *pt_attribute_to_regu (PARSER_CONTEXT * parser, PT_NODE * attr);

static TP_DOMAIN *pt_xasl_data_type_to_domain (PARSER_CONTEXT * parser, const PT_NODE * node);
static DB_VALUE *pt_index_value (const VAL_LIST * value, int index);

static REGU_VARIABLE *pt_join_term_to_regu_variable (PARSER_CONTEXT * parser, PT_NODE * join_term);

static PT_NODE *pt_query_set_reference (PARSER_CONTEXT * parser, PT_NODE * node);

static DB_VALUE *pt_regu_to_dbvalue (PARSER_CONTEXT * parser, REGU_VARIABLE * regu);

#if defined (ENABLE_UNUSED_FUNCTION)
static int look_for_unique_btid (DB_OBJECT * classop, const char *name, BTID * btid);
#endif

static void pt_split_access_if_instnum (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * where,
                    PT_NODE ** access_part, PT_NODE ** if_part, PT_NODE ** instnum_part);

static void pt_split_if_instnum (PARSER_CONTEXT * parser, PT_NODE * where, PT_NODE ** if_part, PT_NODE ** instnum_part);

static void pt_split_having_grbynum (PARSER_CONTEXT * parser, PT_NODE * having, PT_NODE ** having_part,
                     PT_NODE ** grbynum_part);

static int pt_split_attrs (PARSER_CONTEXT * parser, TABLE_INFO * table_info, PT_NODE * pred, PT_NODE ** pred_attrs,
               PT_NODE ** rest_attrs, PT_NODE ** reserved_attrs, int **pred_offsets, int **rest_offsets,
               int **reserved_offsets);

static int pt_split_hash_attrs (PARSER_CONTEXT * parser, TABLE_INFO * table_info, PT_NODE * pred,
                PT_NODE ** build_attrs, PT_NODE ** probe_attrs);

static int pt_split_hash_attrs_for_HQ (PARSER_CONTEXT * parser, PT_NODE * pred, PT_NODE ** build_attrs,
                       PT_NODE ** probe_attrs, PT_NODE ** pred_without_HQ);

static int pt_split_pred_for_HQ (PARSER_CONTEXT * parser, PT_NODE * pred, PT_NODE ** pred_without_HQ,
                 PT_NODE ** pred_with_HQ);

static int pt_to_index_attrs (PARSER_CONTEXT * parser, TABLE_INFO * table_info, QO_XASL_INDEX_INFO * index_pred,
                  PT_NODE * pred, PT_NODE ** pred_attrs, int **pred_offsets);
static int pt_get_pred_attrs (PARSER_CONTEXT * parser, TABLE_INFO * table_info, PT_NODE * pred, PT_NODE ** pred_attrs);

static PT_NODE *pt_flush_class_and_null_xasl (PARSER_CONTEXT * parser, PT_NODE * tree, void *void_arg,
                          int *continue_walk);

static PT_NODE *pt_null_xasl (PARSER_CONTEXT * parser, PT_NODE * tree, void *void_arg, int *continue_walk);

static PT_NODE *pt_is_spec_node (PARSER_CONTEXT * parser, PT_NODE * tree, void *void_arg, int *continue_walk);

static PT_NODE *pt_check_hashable (PARSER_CONTEXT * parser, PT_NODE * tree, void *void_arg, int *continue_walk);

static PT_NODE *pt_find_hq_op_except_prior (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);

static VAL_LIST *pt_clone_val_list (PARSER_CONTEXT * parser, PT_NODE * attribute_list);

static AGGREGATE_TYPE *pt_to_aggregate (PARSER_CONTEXT * parser, PT_NODE * select_node, OUTPTR_LIST * out_list,
                    VAL_LIST * value_list, REGU_VARIABLE_LIST regu_list,
                    REGU_VARIABLE_LIST scan_regu_list, PT_NODE * out_names,
                    DB_VALUE ** grbynum_valp, QO_PLAN * qo_plan);

static SYMBOL_INFO *pt_push_symbol_info (PARSER_CONTEXT * parser, PT_NODE * select_node);

static void pt_pop_symbol_info (PARSER_CONTEXT * parser);

static ACCESS_SPEC_TYPE *pt_make_class_access_spec (PARSER_CONTEXT * parser, PT_NODE * flat, DB_OBJECT * class_,
                            TARGET_TYPE scan_type, ACCESS_METHOD access, INDX_INFO * indexptr,
                            PRED_EXPR * where_key, PRED_EXPR * where_pred,
                            PRED_EXPR * where_range, REGU_VARIABLE_LIST attr_list_key,
                            REGU_VARIABLE_LIST attr_list_pred,
                            REGU_VARIABLE_LIST attr_list_rest,
                            REGU_VARIABLE_LIST attr_list_range,
                            OUTPTR_LIST * output_val_list, REGU_VARIABLE_LIST regu_val_list,
                            HEAP_CACHE_ATTRINFO * cache_key, HEAP_CACHE_ATTRINFO * cache_pred,
                            HEAP_CACHE_ATTRINFO * cache_rest, HEAP_CACHE_ATTRINFO * cache_range,
                            ACCESS_SCHEMA_TYPE schema_type, DB_VALUE ** cache_recordinfo,
                            REGU_VARIABLE_LIST reserved_val_list);

static ACCESS_SPEC_TYPE *pt_make_list_access_spec (XASL_NODE * xasl, ACCESS_METHOD access, INDX_INFO * indexptr,
                           PRED_EXPR * where_pred, REGU_VARIABLE_LIST attr_list_pred,
                           REGU_VARIABLE_LIST attr_list_rest,
                           REGU_VARIABLE_LIST attr_list_build,
                           REGU_VARIABLE_LIST attr_list_probe);

static ACCESS_SPEC_TYPE *pt_make_showstmt_access_spec (PRED_EXPR * where_pred, SHOWSTMT_TYPE show_type,
                               REGU_VARIABLE_LIST arg_list);

static ACCESS_SPEC_TYPE *pt_make_set_access_spec (REGU_VARIABLE * set_expr, ACCESS_METHOD access, INDX_INFO * indexptr,
                          PRED_EXPR * where_pred, REGU_VARIABLE_LIST attr_list);

static ACCESS_SPEC_TYPE *pt_make_cselect_access_spec (XASL_NODE * xasl, PL_SIGNATURE_ARRAY_TYPE * sig_array,
                              ACCESS_METHOD access, INDX_INFO * indexptr,
                              PRED_EXPR * where_pred, REGU_VARIABLE_LIST attr_list);

static SORT_LIST *pt_to_after_iscan (PARSER_CONTEXT * parser, PT_NODE * iscan_list, PT_NODE * root);

static SORT_LIST *pt_to_groupby (PARSER_CONTEXT * parser, PT_NODE * group_list, PT_NODE * root);

static SORT_LIST *pt_to_after_groupby (PARSER_CONTEXT * parser, PT_NODE * group_list, PT_NODE * root);

static TABLE_INFO *pt_find_table_info (UINTPTR spec_id, TABLE_INFO * exposed_list);

static PT_NODE *pt_build_do_stmt_aptr_list_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);

static XASL_NODE *pt_build_do_stmt_aptr_list (PARSER_CONTEXT * parser, PT_NODE * node);

static int pt_is_subquery (PT_NODE * node);

static int *pt_make_identity_offsets (PT_NODE * attr_list);

static void pt_to_pred_terms (PARSER_CONTEXT * parser, PT_NODE * terms, UINTPTR id, PRED_EXPR ** pred);

static VAL_LIST *pt_make_val_list (PARSER_CONTEXT * parser, PT_NODE * attribute_list);

static TABLE_INFO *pt_make_table_info (PARSER_CONTEXT * parser, PT_NODE * table_spec);

static SYMBOL_INFO *pt_symbol_info_alloc (void);

static PRED_EXPR *pt_make_pred_expr_pred (const PRED_EXPR * arg1, const PRED_EXPR * arg2, const BOOL_OP bop);

static XASL_NODE *pt_set_connect_by_xasl (PARSER_CONTEXT * parser, PT_NODE * select_node, XASL_NODE * xasl);

static XASL_NODE *pt_make_connect_by_proc (PARSER_CONTEXT * parser, PT_NODE * select_node, XASL_NODE * select_xasl);

static int pt_add_pseudocolumns_placeholders (PARSER_CONTEXT * parser, OUTPTR_LIST * outptr_list, bool alloc_vals);

static OUTPTR_LIST *pt_make_outlist_from_vallist (PARSER_CONTEXT * parser, VAL_LIST * val_list_p);

static REGU_VARIABLE_LIST pt_make_pos_regu_list (PARSER_CONTEXT * parser, VAL_LIST * val_list_p);

static VAL_LIST *pt_copy_val_list (PARSER_CONTEXT * parser, VAL_LIST * val_list_p);

static int pt_split_pred_regu_list (PARSER_CONTEXT * parser, const VAL_LIST * val_list, const PRED_EXPR * pred,
                    REGU_VARIABLE_LIST * regu_list_rest, REGU_VARIABLE_LIST * regu_list_pred,
                    REGU_VARIABLE_LIST * prior_regu_list_rest,
                    REGU_VARIABLE_LIST * prior_regu_list_pred, bool split_prior);

static void pt_add_regu_var_to_list (REGU_VARIABLE_LIST * destination, REGU_VARIABLE_LIST source);
static void pt_merge_regu_var_lists (REGU_VARIABLE_LIST * destination, REGU_VARIABLE_LIST source);

static PRED_REGU_VARIABLE_P_LIST pt_get_pred_regu_variable_p_list (const PRED_EXPR * pred, int *err);

static PRED_REGU_VARIABLE_P_LIST pt_get_var_regu_variable_p_list (const REGU_VARIABLE * regu, bool is_prior, int *err);

static XASL_NODE *pt_plan_single_table_hq_iterations (PARSER_CONTEXT * parser, PT_NODE * select_node, XASL_NODE * xasl);

static SORT_LIST *pt_to_order_siblings_by (PARSER_CONTEXT * parser, XASL_NODE * xasl, XASL_NODE * connect_by_xasl);
static SORT_LIST *pt_agg_orderby_to_sort_list (PARSER_CONTEXT * parser, PT_NODE * order_list, PT_NODE * agg_args_list);
static PT_NODE *pt_substitute_assigned_name_node (PARSER_CONTEXT * parser, PT_NODE * node, void *arg,
                          int *continue_walk);
static bool pt_is_sort_list_covered (PARSER_CONTEXT * parser, SORT_LIST * covering_list_p, SORT_LIST * covered_list_p);
static int pt_set_limit_optimization_flags (PARSER_CONTEXT * parser, QO_PLAN * plan, XASL_NODE * xasl);
static int pt_set_like_recompile_candidate (PARSER_CONTEXT * parser, QO_PLAN * qo_plan, XASL_NODE * xasl);
static DB_VALUE **pt_make_reserved_value_list (PARSER_CONTEXT * parser, PT_RESERVED_NAME_TYPE type);
static int pt_mvcc_flag_specs_cond_reev (PARSER_CONTEXT * parser, PT_NODE * spec_list, PT_NODE * cond);
static int pt_mvcc_flag_specs_assign_reev (PARSER_CONTEXT * parser, PT_NODE * spec_list, PT_NODE * assign_list);
static int pt_mvcc_set_spec_assign_reev_extra_indexes (PARSER_CONTEXT * parser, PT_NODE * spec_assign,
                               PT_NODE * spec_list, PT_NODE * assign_list, int *indexes,
                               int indexes_alloc_size);
static PT_NODE *pt_mvcc_prepare_upd_del_select (PARSER_CONTEXT * parser, PT_NODE * select_stmt);
static int pt_get_mvcc_reev_range_data (PARSER_CONTEXT * parser, TABLE_INFO * table_info, PT_NODE * where_key_part,
                    QO_XASL_INDEX_INFO * index_pred, PRED_EXPR ** where_range,
                    REGU_VARIABLE_LIST * regu_attributes_range, HEAP_CACHE_ATTRINFO ** cache_range);
static PT_NODE *pt_has_reev_in_subquery_pre (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk);
static PT_NODE *pt_has_reev_in_subquery_post (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk);
static bool pt_has_reev_in_subquery (PARSER_CONTEXT * parser, PT_NODE * statement);

int pt_prepare_corr_subquery_hash_result_cache (PARSER_CONTEXT * parser, PT_NODE * node, XASL_NODE * xasl);
static int pt_make_sq_cache_key_struct (QPROC_DB_VALUE_LIST key_struct, void *p, int type);
static PT_NODE *pt_check_corr_subquery_not_cachable_expr (PARSER_CONTEXT * parser, PT_NODE * node, void *arg,
                              int *continue_walk);
static PT_NODE *pt_make_result_ref (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * groupby_list,
                    VAL_LIST * vallist);

static int pt_check_analytic_limit_optimization (XASL_NODE * xasl, ANALYTIC_EVAL_TYPE * eval_list);
static int pt_count_analytic_covered_sort_list (PARSER_CONTEXT * parser, QO_PLAN * qo_plan, ANALYTIC_EVAL_TYPE * eval,
                        ANALYTIC_INFO * info);

static void
pt_init_xasl_supp_info ()
{
  /* XASL cache related information */
  if (xasl_Supp_info.class_oid_list)
    {
      free_and_init (xasl_Supp_info.class_oid_list);
    }

  if (xasl_Supp_info.class_locks)
    {
      free_and_init (xasl_Supp_info.class_locks);
    }

  if (xasl_Supp_info.tcard_list)
    {
      free_and_init (xasl_Supp_info.tcard_list);
    }

  xasl_Supp_info.n_oid_list = xasl_Supp_info.oid_list_size = 0;
  xasl_Supp_info.includes_tde_class = 0;
}


/*
 * pt_make_connect_by_proc () - makes the XASL of the CONNECT BY node
 *   return:
 *   parser(in):
 *   select_node(in):
 */
static XASL_NODE *
pt_make_connect_by_proc (PARSER_CONTEXT * parser, PT_NODE * select_node, XASL_NODE * select_xasl)
{
  XASL_NODE *xasl, *xptr;
  PT_NODE *from, *where, *if_part, *instnum_part, *build_attrs = NULL, *probe_attrs = NULL;
  PT_NODE *pred_with_HQ = NULL, *pred_without_HQ = NULL;
  QPROC_DB_VALUE_LIST dblist1, dblist2;
  CONNECTBY_PROC_NODE *connect_by;
  int level, flag;
  REGU_VARIABLE_LIST regu_attributes_build, regu_attributes_probe;
  PRED_EXPR *where_without_HQ = NULL;

  if (!parser->symbols)
    {
      return NULL;
    }

  if (!select_node->info.query.q.select.connect_by)
    {
      return NULL;
    }

  /* must not be a merge node */
  if (select_node->info.query.q.select.flavor != PT_USER_SELECT)
    {
      return NULL;
    }

  xasl = regu_xasl_node_alloc (CONNECTBY_PROC);
  if (!xasl)
    {
      goto exit_on_error;
    }

  connect_by = &xasl->proc.connect_by;
  connect_by->single_table_opt = false;

  if (connect_by->start_with_list_id == NULL || connect_by->input_list_id == NULL)
    {
      goto exit_on_error;
    }

  pt_set_level_node_etc (parser, select_node->info.query.q.select.connect_by, &xasl->level_val);

  qo_get_optimization_param (&level, QO_PARAM_LEVEL);
  if (select_node->info.query.q.select.single_table_opt && OPTIMIZATION_ENABLED (level))
    {
      /* handle special case of query without joins */
      PT_NODE *save_where, *save_from;

      save_where = select_node->info.query.q.select.where;
      where = select_node->info.query.q.select.connect_by;

      /* Separate the predicate related to the HQ. */
      /* pred_with_HQ is appended to if_pred instead of data filter. */
      pt_split_pred_for_HQ (parser, where, &pred_without_HQ, &pred_with_HQ);

      select_node->info.query.q.select.where = pred_with_HQ ? pred_without_HQ : where;
      save_from = select_node->info.query.q.select.from->next;
      select_node->info.query.q.select.from->next = NULL;

      xasl = pt_plan_single_table_hq_iterations (parser, select_node, xasl);

      select_node->info.query.q.select.where = save_where;
      select_node->info.query.q.select.from->next = save_from;

      if (xasl == NULL)
    {
      parser_free_tree (parser, pred_without_HQ);
      parser_free_tree (parser, pred_with_HQ);
      PT_INTERNAL_ERROR (parser, "generate hq xasl");
      return NULL;
    }

      connect_by->single_table_opt = true;

      /* pred_with_HQ is appended to if_pred */
      from = select_node->info.query.q.select.from;
      where = pred_with_HQ;
      while (from)
    {
      pt_to_pred_terms (parser, where, from->info.spec.id, &xasl->if_pred);
      from = from->next;
    }
      pt_to_pred_terms (parser, where, 0, &xasl->if_pred);

      parser_free_tree (parser, pred_without_HQ);
      parser_free_tree (parser, pred_with_HQ);
    }
  else
    {
      /* make START WITH pred */

      from = select_node->info.query.q.select.from;
      where = select_node->info.query.q.select.start_with;

      while (from)
    {
      pt_to_pred_terms (parser, where, from->info.spec.id, &connect_by->start_with_pred);
      from = from->next;
    }
      pt_to_pred_terms (parser, where, 0, &connect_by->start_with_pred);

      /* make CONNECT BY pred */

      from = select_node->info.query.q.select.from;
      where = select_node->info.query.q.select.connect_by;

      while (from)
    {
      pt_to_pred_terms (parser, where, from->info.spec.id, &xasl->if_pred);
      from = from->next;
    }
      pt_to_pred_terms (parser, where, 0, &xasl->if_pred);
    }

  /* make after_connect_by_pred */

  from = select_node->info.query.q.select.from;
  pt_set_numbering_node_etc (parser, select_node->info.query.q.select.after_cb_filter, &select_xasl->instnum_val,
                 &select_xasl->ordbynum_val);
  where = parser_copy_tree_list (parser, select_node->info.query.q.select.after_cb_filter);

  pt_split_if_instnum (parser, where, &if_part, &instnum_part);

  /* first set 'etc' field for pseudo-columns, operators and function nodes, to support them in after_connect_by_pred */
  pt_set_level_node_etc (parser, if_part, &select_xasl->level_val);
  pt_set_isleaf_node_etc (parser, if_part, &select_xasl->isleaf_val);
  pt_set_iscycle_node_etc (parser, if_part, &select_xasl->iscycle_val);
  pt_set_connect_by_operator_node_etc (parser, if_part, select_xasl);
  pt_set_qprior_node_etc (parser, if_part, select_xasl);

  while (from)
    {
      pt_to_pred_terms (parser, if_part, from->info.spec.id, &connect_by->after_connect_by_pred);
      from = from->next;
    }
  pt_to_pred_terms (parser, if_part, 0, &connect_by->after_connect_by_pred);

  select_xasl = pt_to_instnum_pred (parser, select_xasl, instnum_part);

  if (if_part)
    {
      parser_free_tree (parser, if_part);
    }
  if (instnum_part)
    {
      parser_free_tree (parser, instnum_part);
    }

  /* make val_list as a list of pointers to all DB_VALUEs of scanners val lists */

  regu_alloc (xasl->val_list);
  if (!xasl->val_list)
    {
      goto exit_on_error;
    }

  dblist2 = NULL;
  xasl->val_list->val_cnt = 0;
  for (xptr = select_xasl; xptr; xptr = xptr->scan_ptr)
    {
      if (xptr->val_list)
    {
      for (dblist1 = xptr->val_list->valp; dblist1; dblist1 = dblist1->next)
        {
          if (!dblist2)
        {
          regu_alloc (xasl->val_list->valp);
          // xasl->val_list->valp = regu_dbvlist_alloc ();      /* don't alloc DB_VALUE */
          dblist2 = xasl->val_list->valp;
        }
          else
        {
          regu_alloc (dblist2->next);
          dblist2 = dblist2->next;
        }

          dblist2->val = dblist1->val;
          dblist2->dom = dblist1->dom;
          xasl->val_list->val_cnt++;
        }
    }
    }

  /* make val_list for use with parent tuple */
  connect_by->prior_val_list = pt_copy_val_list (parser, xasl->val_list);
  if (!connect_by->prior_val_list)
    {
      goto exit_on_error;
    }

  /* make outptr list from val_list */
  xasl->outptr_list = pt_make_outlist_from_vallist (parser, xasl->val_list);
  if (!xasl->outptr_list)
    {
      goto exit_on_error;
    }

  /* make outlist for use with parent tuple */
  connect_by->prior_outptr_list = pt_make_outlist_from_vallist (parser, connect_by->prior_val_list);
  if (!connect_by->prior_outptr_list)
    {
      goto exit_on_error;
    }

  /* make regu_list list from val_list (list of positional regu variables for fetching val_list from a tuple) */
  connect_by->regu_list_rest = pt_make_pos_regu_list (parser, xasl->val_list);

  /* do the same for fetching prior_val_list from parent tuple */
  connect_by->prior_regu_list_rest = pt_make_pos_regu_list (parser, connect_by->prior_val_list);

  /* make regu list for after CONNECT BY iteration */
  connect_by->after_cb_regu_list_rest = pt_make_pos_regu_list (parser, xasl->val_list);

  /* sepparate CONNECT BY predicate regu list; obs: we split prior_regu_list too, for possible future optimizations */
  if (pt_split_pred_regu_list (parser, xasl->val_list, xasl->if_pred, &connect_by->regu_list_rest,
                   &connect_by->regu_list_pred, &connect_by->prior_regu_list_rest,
                   &connect_by->prior_regu_list_pred, true) != NO_ERROR)
    {
      goto exit_on_error;
    }

  /* add spec of list scan for join query */
  if (!connect_by->single_table_opt)
    {
      /* check hashable predicate and split into build and probe attrs */
      where = select_node->info.query.q.select.connect_by;
      if (pt_split_hash_attrs_for_HQ (parser, where, &build_attrs, &probe_attrs, &pred_without_HQ) != NO_ERROR)
    {
      goto exit_on_error;;
    }
      regu_attributes_build = pt_to_regu_variable_list (parser, build_attrs, UNBOX_AS_VALUE, xasl->val_list, NULL);
      regu_attributes_probe = pt_to_regu_variable_list (parser, probe_attrs, UNBOX_AS_VALUE, xasl->val_list, NULL);

      /* make predicate without HQ */
      where_without_HQ = pt_to_pred_expr (parser, pred_without_HQ);

      parser_free_tree (parser, probe_attrs);
      parser_free_tree (parser, build_attrs);
      parser_free_tree (parser, pred_without_HQ);

      /* make list scan spec. */
      xasl->spec_list =
    pt_make_list_access_spec (xasl, ACCESS_METHOD_SEQUENTIAL, NULL, where_without_HQ, connect_by->regu_list_pred,
                  connect_by->regu_list_rest, regu_attributes_build, regu_attributes_probe);
      if (xasl->spec_list == NULL)
    {
      PT_INTERNAL_ERROR (parser, "generate hq(join) xasl");
      return NULL;
    }
      /* if the user asked for NO_HASH_LIST_SCAN, force it on all list scan */
      if (select_node->info.query.q.select.hint & PT_HINT_NO_HASH_LIST_SCAN)
    {
      xasl->spec_list->s.list_node.hash_list_scan_yn = 0;
    }
    }

  /* sepparate after CONNECT BY predicate regu list */
  if (pt_split_pred_regu_list (parser, xasl->val_list, connect_by->after_connect_by_pred,
                   &connect_by->after_cb_regu_list_rest, &connect_by->after_cb_regu_list_pred, NULL, NULL,
                   false) != NO_ERROR)
    {
      goto exit_on_error;
    }

  /* add pseudocols placeholders to outptr_list */
  if (pt_add_pseudocolumns_placeholders (parser, xasl->outptr_list, true) != NO_ERROR)
    {
      goto exit_on_error;
    }

  /* add pseudocols placeholders to prior_outptr_list */
  if (pt_add_pseudocolumns_placeholders (parser, connect_by->prior_outptr_list, false) != NO_ERROR)
    {
      goto exit_on_error;
    }

  /* set NOCYCLE */
  if (select_node->info.query.q.select.check_cycles == CONNECT_BY_CYCLES_NONE)
    {
      XASL_SET_FLAG (xasl, XASL_HAS_NOCYCLE);
    }
  else if (select_node->info.query.q.select.check_cycles == CONNECT_BY_CYCLES_IGNORE
       || select_node->info.query.q.select.check_cycles == CONNECT_BY_CYCLES_NONE_IGNORE)
    {
      XASL_SET_FLAG (xasl, XASL_IGNORE_CYCLES);
    }

  if (pt_has_error (parser))
    {
      return NULL;
    }

  return xasl;

exit_on_error:

  /* the errors here come from memory allocation */
  PT_ERROR (parser, select_node,
        msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));

  return NULL;
}

/*
 * pt_add_pseudocolumns_placeholders() - add placeholders regu vars
 *    for pseudocolumns into outptr_list
 *  return:
 *  outptr_list(in):
 *  alloc_vals(in):
 */
static int
pt_add_pseudocolumns_placeholders (PARSER_CONTEXT * parser, OUTPTR_LIST * outptr_list, bool alloc_vals)
{
  REGU_VARIABLE_LIST regu_list, regu_list_pc;

  if (outptr_list == NULL)
    {
      return ER_FAILED;
    }

  regu_list = outptr_list->valptrp;
  while (regu_list && regu_list->next)
    {
      regu_list = regu_list->next;
    }

  /* add parent pos pseudocolumn placeholder */

  outptr_list->valptr_cnt++;

  regu_alloc (regu_list_pc);
  if (regu_list_pc == NULL)
    {
      return ER_FAILED;
    }

  if (regu_list)
    {
      regu_list->next = regu_list_pc;
    }
  else
    {
      regu_list = outptr_list->valptrp = regu_list_pc;
    }

  regu_list_pc->next = NULL;
  regu_list_pc->value.type = TYPE_CONSTANT;
  regu_list_pc->value.domain = &tp_Bit_domain;
  if (alloc_vals)
    {
      regu_alloc (regu_list_pc->value.value.dbvalptr);
      if (!regu_list_pc->value.value.dbvalptr)
    {
      return ER_FAILED;
    }
      pt_register_orphan_db_value (parser, regu_list_pc->value.value.dbvalptr);
    }
  else
    {
      regu_list_pc->value.value.dbvalptr = NULL;
    }

  /* add string placeholder for computing node's path from parent */

  outptr_list->valptr_cnt++;
  if (regu_list->next)
    {
      regu_list = regu_list->next;
    }

  regu_alloc (regu_list_pc);
  if (regu_list_pc == NULL)
    {
      return ER_FAILED;
    }

  regu_list_pc->next = NULL;
  regu_list_pc->value.type = TYPE_CONSTANT;
  regu_list_pc->value.domain = &tp_String_domain;
  if (alloc_vals)
    {
      regu_alloc (regu_list_pc->value.value.dbvalptr);
      if (!regu_list_pc->value.value.dbvalptr)
    {
      return ER_FAILED;
    }
      pt_register_orphan_db_value (parser, regu_list_pc->value.value.dbvalptr);
    }
  else
    {
      regu_list_pc->value.value.dbvalptr = NULL;
    }

  regu_list->next = regu_list_pc;

  /* add LEVEL placeholder */

  outptr_list->valptr_cnt++;
  regu_list = regu_list->next;

  regu_alloc (regu_list_pc);
  if (regu_list_pc == NULL)
    {
      return ER_FAILED;
    }

  regu_list->next = regu_list_pc;

  regu_list_pc->next = NULL;
  regu_list_pc->value.type = TYPE_CONSTANT;
  regu_list_pc->value.domain = &tp_Integer_domain;
  if (alloc_vals)
    {
      regu_alloc (regu_list_pc->value.value.dbvalptr);
      if (!regu_list_pc->value.value.dbvalptr)
    {
      return ER_FAILED;
    }
      pt_register_orphan_db_value (parser, regu_list_pc->value.value.dbvalptr);
    }
  else
    {
      regu_list_pc->value.value.dbvalptr = NULL;
    }

  /* add CONNECT_BY_ISLEAF placeholder */

  outptr_list->valptr_cnt++;
  regu_list = regu_list->next;

  regu_alloc (regu_list_pc);
  if (regu_list_pc == NULL)
    {
      return ER_FAILED;
    }

  regu_list->next = regu_list_pc;

  regu_list_pc->next = NULL;
  regu_list_pc->value.type = TYPE_CONSTANT;
  regu_list_pc->value.domain = &tp_Integer_domain;
  if (alloc_vals)
    {
      regu_alloc (regu_list_pc->value.value.dbvalptr);
      if (!regu_list_pc->value.value.dbvalptr)
    {
      return ER_FAILED;
    }
      pt_register_orphan_db_value (parser, regu_list_pc->value.value.dbvalptr);
    }
  else
    {
      regu_list_pc->value.value.dbvalptr = NULL;
    }

  /* add CONNECT_BY_ISCYCLE placeholder */

  outptr_list->valptr_cnt++;
  regu_list = regu_list->next;

  regu_alloc (regu_list_pc);
  if (regu_list_pc == NULL)
    {
      return ER_FAILED;
    }

  regu_list->next = regu_list_pc;

  regu_list_pc->next = NULL;
  regu_list_pc->value.type = TYPE_CONSTANT;
  regu_list_pc->value.domain = &tp_Integer_domain;
  if (alloc_vals)
    {
      regu_alloc (regu_list_pc->value.value.dbvalptr);
      if (!regu_list_pc->value.value.dbvalptr)
    {
      return ER_FAILED;
    }
      pt_register_orphan_db_value (parser, regu_list_pc->value.value.dbvalptr);
    }
  else
    {
      regu_list_pc->value.value.dbvalptr = NULL;
    }

  return NO_ERROR;
}

/*
 * pt_plan_single_table_hq_iterations () - makes plan for single table
 *                     hierarchical query iterations
 *   return:
 *   select_node(in):
 *   xasl(in):
 */
static XASL_NODE *
pt_plan_single_table_hq_iterations (PARSER_CONTEXT * parser, PT_NODE * select_node, XASL_NODE * xasl)
{
  QO_PLAN *plan;
  int level;

  plan = qo_optimize_query (parser, select_node);

  if (!plan && select_node->info.query.q.select.hint != PT_HINT_NONE)
    {
      PT_NODE *leading, *use_nl, *use_idx, *index_ss, *index_ls, *use_merge, *no_use_hash, *use_hash;
      PT_HINT_ENUM hint;
      const char *alias_print;

      /* save hint information */
      hint = select_node->info.query.q.select.hint;
      select_node->info.query.q.select.hint = PT_HINT_NONE;

      leading = select_node->info.query.q.select.leading;
      select_node->info.query.q.select.leading = NULL;

      use_nl = select_node->info.query.q.select.use_nl;
      select_node->info.query.q.select.use_nl = NULL;

      use_idx = select_node->info.query.q.select.use_idx;
      select_node->info.query.q.select.use_idx = NULL;

      index_ss = select_node->info.query.q.select.index_ss;
      select_node->info.query.q.select.index_ss = NULL;

      index_ls = select_node->info.query.q.select.index_ls;
      select_node->info.query.q.select.index_ls = NULL;

      use_merge = select_node->info.query.q.select.use_merge;
      select_node->info.query.q.select.use_merge = NULL;

      no_use_hash = select_node->info.query.q.select.no_use_hash;
      select_node->info.query.q.select.no_use_hash = NULL;

      use_hash = select_node->info.query.q.select.use_hash;
      select_node->info.query.q.select.use_hash = NULL;

      alias_print = select_node->alias_print;
      select_node->alias_print = NULL;

      /* retry optimization */
      plan = qo_optimize_query (parser, select_node);

      /* restore hint information */
      select_node->info.query.q.select.hint = hint;
      select_node->info.query.q.select.leading = leading;
      select_node->info.query.q.select.use_nl = use_nl;
      select_node->info.query.q.select.use_idx = use_idx;
      select_node->info.query.q.select.index_ss = index_ss;
      select_node->info.query.q.select.index_ls = index_ls;
      select_node->info.query.q.select.use_merge = use_merge;
      select_node->info.query.q.select.no_use_hash = no_use_hash;
      select_node->info.query.q.select.use_hash = use_hash;

      select_node->alias_print = alias_print;
    }

  if (!plan)
    {
      return NULL;
    }

  xasl = qo_add_hq_iterations_access_spec (plan, xasl);

  if (xasl != NULL)
    {
      /* dump plan */
      qo_get_optimization_param (&level, QO_PARAM_LEVEL);
      if (level >= 0x100 && plan)
    {
      fputs ("\nPlan for single table hierarchical iterations:\n", db_query_get_plan_dump_fp ());
      qo_plan_dump (plan, db_query_get_plan_dump_fp ());
    }
    }

  /* discard plan */
  qo_plan_discard (plan);

  return xasl;
}

/*
 * pt_make_pred_expr_pred () - makes a pred expr logical node (AND/OR)
 *   return:
 *   arg1(in):
 *   arg2(in):
 *   bop(in):
 */
static PRED_EXPR *
pt_make_pred_expr_pred (const PRED_EXPR * arg1, const PRED_EXPR * arg2, const BOOL_OP bop)
{
  PRED_EXPR *pred = NULL;

  if (arg1 != NULL && arg2 != NULL)
    {
      regu_alloc (pred);

      if (pred)
    {
      pred->type = T_PRED;
      pred->pe.m_pred.lhs = (PRED_EXPR *) arg1;
      pred->pe.m_pred.rhs = (PRED_EXPR *) arg2;
      pred->pe.m_pred.bool_op = bop;
    }
    }

  return pred;
}

/*
 * pt_make_pred_term_not () - makes a pred expr one argument term (NOT)
 *   return:
 *   arg1(in):
 *
 * Note :
 * This can make a predicate term for an indirect term
 */
static PRED_EXPR *
pt_make_pred_term_not (const PRED_EXPR * arg1)
{
  PRED_EXPR *pred = NULL;

  if (arg1 != NULL)
    {
      regu_alloc (pred);

      if (pred)
    {
      pred->type = T_NOT_TERM;
      pred->pe.m_not_term = (PRED_EXPR *) arg1;
    }
    }

  return pred;
}


/*
 * pt_make_pred_term_comp () - makes a pred expr term comparison node
 *   return:
 *   arg1(in):
 *   arg2(in):
 *   rop(in):
 *   data_type(in):
 */
static PRED_EXPR *
pt_make_pred_term_comp (const REGU_VARIABLE * arg1, const REGU_VARIABLE * arg2, const REL_OP rop,
            const DB_TYPE data_type)
{
  PRED_EXPR *pred = NULL;

  if (arg1 != NULL && (arg2 != NULL || rop == R_EXISTS || rop == R_NULL))
    {
      regu_alloc (pred);

      if (pred)
    {
      COMP_EVAL_TERM *et_comp = &pred->pe.m_eval_term.et.et_comp;

      pred->type = T_EVAL_TERM;
      pred->pe.m_eval_term.et_type = T_COMP_EVAL_TERM;
      et_comp->lhs = (REGU_VARIABLE *) arg1;
      et_comp->rhs = (REGU_VARIABLE *) arg2;
      et_comp->rel_op = rop;
      et_comp->type = data_type;
    }
    }

  return pred;
}

/*
 * pt_make_pred_term_some_all () - makes a pred expr term some/all
 *                 comparison node
 *   return:
 *   arg1(in):
 *   arg2(in):
 *   rop(in):
 *   data_type(in):
 *   some_all(in):
 */
static PRED_EXPR *
pt_make_pred_term_some_all (const REGU_VARIABLE * arg1, const REGU_VARIABLE * arg2, const REL_OP rop,
                const DB_TYPE data_type, const QL_FLAG some_all)
{
  PRED_EXPR *pred = NULL;

  if (arg1 != NULL && arg2 != NULL)
    {
      regu_alloc (pred);

      if (pred)
    {
      ALSM_EVAL_TERM *et_alsm = &pred->pe.m_eval_term.et.et_alsm;

      pred->type = T_EVAL_TERM;
      pred->pe.m_eval_term.et_type = T_ALSM_EVAL_TERM;
      et_alsm->elem = (REGU_VARIABLE *) arg1;
      et_alsm->elemset = (REGU_VARIABLE *) arg2;
      et_alsm->rel_op = rop;
      et_alsm->item_type = data_type;
      et_alsm->eq_flag = some_all;
    }
    }

  return pred;
}

/*
 * pt_make_pred_term_like () - makes a pred expr term like comparison node
 *   return:
 *   arg1(in):
 *   arg2(in):
 *   esc(in):
 */
static PRED_EXPR *
pt_make_pred_term_like (const REGU_VARIABLE * arg1, const REGU_VARIABLE * arg2, const REGU_VARIABLE * arg3)
{
  PRED_EXPR *pred = NULL;

  if (arg1 != NULL && arg2 != NULL)
    {
      regu_alloc (pred);

      if (pred)
    {
      LIKE_EVAL_TERM *et_like = &pred->pe.m_eval_term.et.et_like;

      pred->type = T_EVAL_TERM;
      pred->pe.m_eval_term.et_type = T_LIKE_EVAL_TERM;
      et_like->src = (REGU_VARIABLE *) arg1;
      et_like->pattern = (REGU_VARIABLE *) arg2;
      et_like->esc_char = (REGU_VARIABLE *) arg3;
    }
    }

  return pred;
}

/*
 * pt_make_pred_term_rlike () - makes a pred expr term of regex comparison node
 *   return: predicate expression
 *   arg1(in): source string regu var
 *   arg2(in): pattern regu var
 *   case_sensitive(in): sensitivity flag regu var
 */
static PRED_EXPR *
pt_make_pred_term_rlike (REGU_VARIABLE * arg1, REGU_VARIABLE * arg2, REGU_VARIABLE * case_sensitive)
{
  PRED_EXPR *pred = NULL;
  RLIKE_EVAL_TERM *et_rlike = NULL;

  if (arg1 == NULL || arg2 == NULL || case_sensitive == NULL)
    {
      return NULL;
    }

  regu_alloc (pred);
  if (pred == NULL)
    {
      return NULL;
    }

  et_rlike = &pred->pe.m_eval_term.et.et_rlike;
  pred->type = T_EVAL_TERM;
  pred->pe.m_eval_term.et_type = T_RLIKE_EVAL_TERM;
  et_rlike->src = arg1;
  et_rlike->pattern = arg2;
  et_rlike->case_sensitive = case_sensitive;
  et_rlike->compiled_regex = NULL;

  return pred;
}

/*
 * pt_make_pred_term_is () - makes a pred expr term for IS/IS NOT
 *     return:
 *   parser(in):
 *   arg1(in):
 *   arg2(in):
 *   op(in):
 *
 */
static PRED_EXPR *
pt_make_pred_term_is (PARSER_CONTEXT * parser, PT_NODE * arg1, PT_NODE * arg2, const BOOL_OP bop)
{
  PT_NODE *dummy1, *dummy2;
  PRED_EXPR *pred_rhs, *pred = NULL;
  DB_TYPE data_type;

  if (arg1 != NULL && arg2 != NULL)
    {
      dummy1 = parser_new_node (parser, PT_VALUE);
      dummy2 = parser_new_node (parser, PT_VALUE);

      if (dummy1 && dummy2)
    {
      dummy2->type_enum = PT_TYPE_INTEGER;
      dummy2->info.value.data_value.i = 1;

      if (arg2->type_enum == PT_TYPE_LOGICAL)
        {
          /* term for TRUE/FALSE */
          dummy1->type_enum = PT_TYPE_INTEGER;
          dummy1->info.value.data_value.i = arg2->info.value.data_value.i;
          data_type = DB_TYPE_INTEGER;
        }
      else
        {
          /* term for UNKNOWN */
          dummy1->type_enum = PT_TYPE_NULL;
          data_type = DB_TYPE_NULL;
        }

      /* make a R_EQ pred term for rhs boolean val */
      pred_rhs =
        pt_make_pred_term_comp (pt_to_regu_variable (parser, dummy1, UNBOX_AS_VALUE),
                    pt_to_regu_variable (parser, dummy2, UNBOX_AS_VALUE), R_EQ, data_type);

      pred = pt_make_pred_expr_pred (pt_to_pred_expr (parser, arg1), pred_rhs, bop);
    }
      else
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
    }
    }

  return pred;
}


/*
 * pt_to_pred_expr_local_with_arg () - converts a parse expression tree
 *                     to pred expressions
 *   return: A NULL return indicates an error occurred
 *   parser(in):
 *   node(in): should be something that will evaluate into a boolean
 *   argp(out):
 */
static PRED_EXPR *
pt_to_pred_expr_local_with_arg (PARSER_CONTEXT * parser, PT_NODE * node, int *argp)
{
  PRED_EXPR *pred = NULL;
  DB_TYPE data_type;
  void *saved_etc;
  int dummy;
  PT_NODE *save_node;
  REGU_VARIABLE *regu_var1 = NULL, *regu_var2 = NULL, *regu_var3 = NULL;

  if (!argp)
    {
      argp = &dummy;
    }

  if (node)
    {
      save_node = node;

      CAST_POINTER_TO_NODE (node);

      if (node->node_type == PT_EXPR)
    {
      if (node->info.expr.arg1 && node->info.expr.arg2
          && (node->info.expr.arg1->type_enum == node->info.expr.arg2->type_enum))
        {
          data_type = pt_node_to_db_type (node->info.expr.arg1);
        }
      else
        {
          data_type = DB_TYPE_NULL; /* let the back end figure it out */
        }

      /* to get information for inst_num() scan typr from pt_to_regu_variable(), borrow 'parser->etc' field */
      saved_etc = parser->etc;
      parser->etc = NULL;

      /* set regu variables */
      if (node->info.expr.op == PT_SETEQ || node->info.expr.op == PT_EQ || node->info.expr.op == PT_SETNEQ
          || node->info.expr.op == PT_NE || node->info.expr.op == PT_GE || node->info.expr.op == PT_GT
          || node->info.expr.op == PT_LT || node->info.expr.op == PT_LE || node->info.expr.op == PT_SUBSET
          || node->info.expr.op == PT_SUBSETEQ || node->info.expr.op == PT_SUPERSET
          || node->info.expr.op == PT_SUPERSETEQ || node->info.expr.op == PT_NULLSAFE_EQ)
        {
          regu_var1 = pt_to_regu_variable (parser, node->info.expr.arg1, UNBOX_AS_VALUE);
          regu_var2 = pt_to_regu_variable (parser, node->info.expr.arg2, UNBOX_AS_VALUE);
        }
      else if (node->info.expr.op == PT_IS_NOT_IN || node->info.expr.op == PT_IS_IN
           || node->info.expr.op == PT_EQ_SOME || node->info.expr.op == PT_NE_SOME
           || node->info.expr.op == PT_GE_SOME || node->info.expr.op == PT_GT_SOME
           || node->info.expr.op == PT_LT_SOME || node->info.expr.op == PT_LE_SOME
           || node->info.expr.op == PT_EQ_ALL || node->info.expr.op == PT_NE_ALL
           || node->info.expr.op == PT_GE_ALL || node->info.expr.op == PT_GT_ALL
           || node->info.expr.op == PT_LT_ALL || node->info.expr.op == PT_LE_ALL)
        {
          regu_var1 = pt_to_regu_variable (parser, node->info.expr.arg1, UNBOX_AS_VALUE);
          regu_var2 = pt_to_regu_variable (parser, node->info.expr.arg2, UNBOX_AS_TABLE);
        }

      switch (node->info.expr.op)
        {
          /* Logical operators */
        case PT_AND:
          pred =
        pt_make_pred_expr_pred (pt_to_pred_expr (parser, node->info.expr.arg1),
                    pt_to_pred_expr (parser, node->info.expr.arg2), B_AND);
          break;

        case PT_OR:
          /* set information for inst_num() scan type */
          *argp |= PT_PRED_ARG_INSTNUM_CONTINUE;
          *argp |= PT_PRED_ARG_GRBYNUM_CONTINUE;
          *argp |= PT_PRED_ARG_ORDBYNUM_CONTINUE;
          pred =
        pt_make_pred_expr_pred (pt_to_pred_expr (parser, node->info.expr.arg1),
                    pt_to_pred_expr (parser, node->info.expr.arg2), B_OR);
          break;

        case PT_NOT:
          /* We cannot certain what we have to do if NOT predicate set information for inst_num() scan type */
          *argp |= PT_PRED_ARG_INSTNUM_CONTINUE;
          *argp |= PT_PRED_ARG_GRBYNUM_CONTINUE;
          *argp |= PT_PRED_ARG_ORDBYNUM_CONTINUE;
          pred = pt_make_pred_term_not (pt_to_pred_expr (parser, node->info.expr.arg1));
          break;

          /* one to one comparisons */
        case PT_SETEQ:
        case PT_EQ:
          pred =
        pt_make_pred_term_comp (regu_var1, regu_var2,
                    ((node->info.expr.qualifier == PT_EQ_TORDER) ? R_EQ_TORDER : R_EQ), data_type);
          break;

        case PT_NULLSAFE_EQ:
          pred = pt_make_pred_term_comp (regu_var1, regu_var2, R_NULLSAFE_EQ, data_type);
          break;

        case PT_IS:
          pred = pt_make_pred_term_is (parser, node->info.expr.arg1, node->info.expr.arg2, B_IS);
          break;

        case PT_IS_NOT:
          pred = pt_make_pred_term_is (parser, node->info.expr.arg1, node->info.expr.arg2, B_IS_NOT);
          break;

        case PT_ISNULL:
          regu_var1 = pt_to_regu_variable (parser, node->info.expr.arg1, UNBOX_AS_VALUE);
          pred = pt_make_pred_term_comp (regu_var1, NULL, R_NULL, data_type);
          break;

        case PT_XOR:
          pred =
        pt_make_pred_expr_pred (pt_to_pred_expr (parser, node->info.expr.arg1),
                    pt_to_pred_expr (parser, node->info.expr.arg2), B_XOR);
          break;

        case PT_SETNEQ:
        case PT_NE:
          /* We cannot certain what we have to do if NOT predicate */
          /* set information for inst_num() scan type */
          *argp |= PT_PRED_ARG_INSTNUM_CONTINUE;
          *argp |= PT_PRED_ARG_GRBYNUM_CONTINUE;
          *argp |= PT_PRED_ARG_ORDBYNUM_CONTINUE;
          pred = pt_make_pred_term_comp (regu_var1, regu_var2, R_NE, data_type);
          break;

        case PT_GE:
          pred = pt_make_pred_term_comp (regu_var1, regu_var2, R_GE, data_type);
          break;

        case PT_GT:
          pred = pt_make_pred_term_comp (regu_var1, regu_var2, R_GT, data_type);
          break;

        case PT_LT:
          pred = pt_make_pred_term_comp (regu_var1, regu_var2, R_LT, data_type);
          break;

        case PT_LE:
          pred = pt_make_pred_term_comp (regu_var1, regu_var2, R_LE, data_type);
          break;

        case PT_SUBSET:
          pred = pt_make_pred_term_comp (regu_var1, regu_var2, R_SUBSET, data_type);
          break;

        case PT_SUBSETEQ:
          pred = pt_make_pred_term_comp (regu_var1, regu_var2, R_SUBSETEQ, data_type);
          break;

        case PT_SUPERSET:
          pred = pt_make_pred_term_comp (regu_var1, regu_var2, R_SUPERSET, data_type);
          break;

        case PT_SUPERSETEQ:
          pred = pt_make_pred_term_comp (regu_var1, regu_var2, R_SUPERSETEQ, data_type);
          break;

        case PT_EXISTS:
          regu_var1 = pt_to_regu_variable (parser, node->info.expr.arg1, UNBOX_AS_TABLE);
          if (regu_var1->xasl && pt_prepare_corr_subquery_hash_result_cache
          (parser, (PT_NODE *) node->info.expr.arg1, regu_var1->xasl))
        {
          XASL_SET_FLAG (regu_var1->xasl, XASL_USES_SQ_CACHE);
        }
          pred = pt_make_pred_term_comp (regu_var1, NULL, R_EXISTS, data_type);

          /* exists op must fetch one tuple */
          if (!pt_has_having_with_predicate (parser, node->info.expr.arg1) && regu_var1 && regu_var1->xasl)
        {
          XASL_SET_FLAG (regu_var1->xasl, XASL_NEED_SINGLE_TUPLE_SCAN);
        }
          break;

        case PT_IS_NULL:
        case PT_IS_NOT_NULL:
          regu_var1 = pt_to_regu_variable (parser, node->info.expr.arg1, UNBOX_AS_VALUE);
          pred = pt_make_pred_term_comp (regu_var1, NULL, R_NULL, data_type);

          if (node->info.expr.op == PT_IS_NOT_NULL)
        {
          pred = pt_make_pred_term_not (pred);
        }
          break;

        case PT_NOT_BETWEEN:
          /* set information for inst_num() scan type */
          *argp |= PT_PRED_ARG_INSTNUM_CONTINUE;
          *argp |= PT_PRED_ARG_GRBYNUM_CONTINUE;
          *argp |= PT_PRED_ARG_ORDBYNUM_CONTINUE;
          [[fallthrough]];

        case PT_BETWEEN:
        case PT_RANGE:
          /* set information for inst_num() scan type */
          if (node->info.expr.arg2 && node->info.expr.arg2->or_next)
        {
          *argp |= PT_PRED_ARG_INSTNUM_CONTINUE;
          *argp |= PT_PRED_ARG_GRBYNUM_CONTINUE;
          *argp |= PT_PRED_ARG_ORDBYNUM_CONTINUE;
        }

          {
        PT_NODE *arg1, *arg2, *lower, *upper;
        PRED_EXPR *pred1, *pred2;
        REGU_VARIABLE *regu;
        REL_OP op1 = R_NONE, op2 = R_NONE;

        arg1 = node->info.expr.arg1;
        regu = pt_to_regu_variable (parser, arg1, UNBOX_AS_VALUE);

        /* only PT_RANGE has 'or_next' link; PT_BETWEEN and PT_NOT_BETWEEN do not have 'or_next' */

        /* for each range spec of RANGE node */
        for (arg2 = node->info.expr.arg2; arg2; arg2 = arg2->or_next)
          {
            if (!arg2 || arg2->node_type != PT_EXPR || !pt_is_between_range_op (arg2->info.expr.op))
              {
            /* error! */
            break;
              }
            lower = arg2->info.expr.arg1;
            upper = arg2->info.expr.arg2;

            switch (arg2->info.expr.op)
              {
              case PT_BETWEEN_AND:
              case PT_BETWEEN_GE_LE:
            op1 = R_GE;
            op2 = R_LE;
            break;
              case PT_BETWEEN_GE_LT:
            op1 = R_GE;
            op2 = R_LT;
            break;
              case PT_BETWEEN_GT_LE:
            op1 = R_GT;
            op2 = R_LE;
            break;
              case PT_BETWEEN_GT_LT:
            op1 = R_GT;
            op2 = R_LT;
            break;
              case PT_BETWEEN_EQ_NA:
            /* special case; if this range spec is derived from '=' or 'IN' */
            op1 = R_EQ;
            op2 = (REL_OP) 0;
            break;
              case PT_BETWEEN_INF_LE:
            op1 = R_LE;
            op2 = (REL_OP) 0;
            break;
              case PT_BETWEEN_INF_LT:
            op1 = R_LT;
            op2 = (REL_OP) 0;
            break;
              case PT_BETWEEN_GE_INF:
            op1 = R_GE;
            op2 = (REL_OP) 0;
            break;
              case PT_BETWEEN_GT_INF:
            op1 = R_GT;
            op2 = (REL_OP) 0;
            break;
              default:
            break;
              }

            if (op1)
              {
            regu_var1 = pt_to_regu_variable (parser, lower, UNBOX_AS_VALUE);
            pred1 = pt_make_pred_term_comp (regu, regu_var1, op1, data_type);
              }
            else
              {
            pred1 = NULL;
              }

            if (op2)
              {
            regu_var2 = pt_to_regu_variable (parser, upper, UNBOX_AS_VALUE);
            pred2 = pt_make_pred_term_comp (regu, regu_var2, op2, data_type);
              }
            else
              {
            pred2 = NULL;
              }

            /* make AND predicate of both two expressions */
            if (pred1 && pred2)
              {
            pred1 = pt_make_pred_expr_pred (pred1, pred2, B_AND);
              }

            /* make NOT predicate of BETWEEN predicate */
            if (node->info.expr.op == PT_NOT_BETWEEN)
              {
            pred1 = pt_make_pred_term_not (pred1);
              }

            /* make OR predicate */
            pred = (pred) ? pt_make_pred_expr_pred (pred1, pred, B_OR) : pred1;
          }     /* for (arg2 = node->info.expr.arg2; ...) */
          }
          break;

          /* one to many comparisons */
        case PT_IS_NOT_IN:
        case PT_IS_IN:
        case PT_EQ_SOME:
          /* set information for inst_num() scan type */
          *argp |= PT_PRED_ARG_INSTNUM_CONTINUE;
          *argp |= PT_PRED_ARG_GRBYNUM_CONTINUE;
          *argp |= PT_PRED_ARG_ORDBYNUM_CONTINUE;
          pred = pt_make_pred_term_some_all (regu_var1, regu_var2, R_EQ, data_type, F_SOME);

          if (node->info.expr.op == PT_IS_NOT_IN)
        {
          pred = pt_make_pred_term_not (pred);
        }
          break;

        case PT_NE_SOME:
          /* set information for inst_num() scan type */
          *argp |= PT_PRED_ARG_INSTNUM_CONTINUE;
          *argp |= PT_PRED_ARG_GRBYNUM_CONTINUE;
          *argp |= PT_PRED_ARG_ORDBYNUM_CONTINUE;
          pred = pt_make_pred_term_some_all (regu_var1, regu_var2, R_NE, data_type, F_SOME);
          break;

        case PT_GE_SOME:
          pred = pt_make_pred_term_some_all (regu_var1, regu_var2, R_GE, data_type, F_SOME);
          break;

        case PT_GT_SOME:
          pred = pt_make_pred_term_some_all (regu_var1, regu_var2, R_GT, data_type, F_SOME);
          break;

        case PT_LT_SOME:
          pred = pt_make_pred_term_some_all (regu_var1, regu_var2, R_LT, data_type, F_SOME);
          break;

        case PT_LE_SOME:
          pred = pt_make_pred_term_some_all (regu_var1, regu_var2, R_LE, data_type, F_SOME);
          break;

        case PT_EQ_ALL:
          pred = pt_make_pred_term_some_all (regu_var1, regu_var2, R_EQ, data_type, F_ALL);
          break;

        case PT_NE_ALL:
          pred = pt_make_pred_term_some_all (regu_var1, regu_var2, R_NE, data_type, F_ALL);
          break;

        case PT_GE_ALL:
          pred = pt_make_pred_term_some_all (regu_var1, regu_var2, R_GE, data_type, F_ALL);
          break;

        case PT_GT_ALL:
          pred = pt_make_pred_term_some_all (regu_var1, regu_var2, R_GT, data_type, F_ALL);
          break;

        case PT_LT_ALL:
          pred = pt_make_pred_term_some_all (regu_var1, regu_var2, R_LT, data_type, F_ALL);
          break;

        case PT_LE_ALL:
          pred = pt_make_pred_term_some_all (regu_var1, regu_var2, R_LE, data_type, F_ALL);
          break;

          /* like comparison */
        case PT_NOT_LIKE:
        case PT_LIKE:
          /* set information for inst_num() scan type */
          *argp |= PT_PRED_ARG_INSTNUM_CONTINUE;
          *argp |= PT_PRED_ARG_GRBYNUM_CONTINUE;
          *argp |= PT_PRED_ARG_ORDBYNUM_CONTINUE;
          {
        REGU_VARIABLE *regu_escape = NULL;
        PT_NODE *arg2 = node->info.expr.arg2;

        regu_var1 = pt_to_regu_variable (parser, node->info.expr.arg1, UNBOX_AS_VALUE);

        if (arg2 && arg2->node_type == PT_EXPR && arg2->info.expr.op == PT_LIKE_ESCAPE)
          {
            /* this should be an escape character expression */
            if ((arg2->info.expr.arg2->node_type != PT_VALUE)
            && (arg2->info.expr.arg2->node_type != PT_HOST_VAR))
              {
            PT_ERRORm (parser, arg2, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_WANT_ESC_LIT_STRING);
            break;
              }

            regu_escape = pt_to_regu_variable (parser, arg2->info.expr.arg2, UNBOX_AS_VALUE);
            arg2 = arg2->info.expr.arg1;
          }
        else if (prm_get_bool_value (PRM_ID_REQUIRE_LIKE_ESCAPE_CHARACTER))
          {
            PT_NODE *arg1 = node->info.expr.arg1;
            PT_NODE *node = pt_make_string_value (parser, "\\");

            assert (!prm_get_bool_value (PRM_ID_NO_BACKSLASH_ESCAPES));

            regu_escape = pt_to_regu_variable (parser, node, UNBOX_AS_VALUE);
            parser_free_node (parser, node);
          }

        regu_var2 = pt_to_regu_variable (parser, arg2, UNBOX_AS_VALUE);

        pred = pt_make_pred_term_like (regu_var1, regu_var2, regu_escape);

        if (node->info.expr.op == PT_NOT_LIKE)
          {
            pred = pt_make_pred_term_not (pred);
          }
          }
          break;

          /* regex like comparison */
        case PT_RLIKE:
        case PT_NOT_RLIKE:
        case PT_RLIKE_BINARY:
        case PT_NOT_RLIKE_BINARY:
          /* set information for inst_num() scan type */
          *argp |= PT_PRED_ARG_INSTNUM_CONTINUE;
          *argp |= PT_PRED_ARG_GRBYNUM_CONTINUE;
          *argp |= PT_PRED_ARG_ORDBYNUM_CONTINUE;
          {
        regu_var1 = pt_to_regu_variable (parser, node->info.expr.arg1, UNBOX_AS_VALUE);

        regu_var2 = pt_to_regu_variable (parser, node->info.expr.arg2, UNBOX_AS_VALUE);

        regu_var3 = pt_to_regu_variable (parser, node->info.expr.arg3, UNBOX_AS_VALUE);

        pred = pt_make_pred_term_rlike (regu_var1, regu_var2, regu_var3);

        if (node->info.expr.op == PT_NOT_RLIKE || node->info.expr.op == PT_NOT_RLIKE_BINARY)
          {
            pred = pt_make_pred_term_not (pred);
          }
          }
          break;

          /* this is an error ! */
        default:
          pred = NULL;
          break;
        }           /* switch (node->info.expr.op) */

      /* to get information for inst_num() scan typr from pt_to_regu_variable(), borrow 'parser->etc' field */
      if (parser->etc)
        {
          *argp |= PT_PRED_ARG_INSTNUM_CONTINUE;
          *argp |= PT_PRED_ARG_GRBYNUM_CONTINUE;
          *argp |= PT_PRED_ARG_ORDBYNUM_CONTINUE;
        }

      parser->etc = saved_etc;
    }
      else if (node->node_type == PT_HOST_VAR)
    {
      /* It should be ( ? ). */
      /* The predicate expression is ( ( ? <> 0 ) ). */

      PT_NODE *arg2;
      bool is_logical = false;

      /* we may have type_enum set to PT_TYPE_LOGICAL by type checking, if this is the case set it to
       * PT_TYPE_INTEGER to avoid recursion */
      if (node->type_enum == PT_TYPE_LOGICAL)
        {
          node->type_enum = PT_TYPE_INTEGER;
          is_logical = true;
        }

      arg2 = parser_new_node (parser, PT_VALUE);

      if (arg2)
        {
          arg2->type_enum = PT_TYPE_INTEGER;
          arg2->info.value.data_value.i = 0;
          data_type = DB_TYPE_INTEGER;

          regu_var1 = pt_to_regu_variable (parser, node, UNBOX_AS_VALUE);
          regu_var2 = pt_to_regu_variable (parser, arg2, UNBOX_AS_VALUE);
          pred = pt_make_pred_term_comp (regu_var1, regu_var2, R_NE, data_type);
        }
      else
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
        }

      /* restore original type */
      if (is_logical)
        {
          node->type_enum = PT_TYPE_LOGICAL;
        }
    }
      else if (PT_IS_POINTER_REF_NODE (node))
    {
      /* reference pointer node */
      PT_NODE *zero, *real_node;

      real_node = node->info.pointer.node;
      CAST_POINTER_TO_NODE (real_node);

      if (real_node != NULL && real_node->type_enum == PT_TYPE_LOGICAL)
        {
          zero = parser_new_node (parser, PT_VALUE);

          if (zero != NULL)
        {
          zero->type_enum = PT_TYPE_INTEGER;
          zero->info.value.data_value.i = 0;

          data_type = DB_TYPE_INTEGER;

          regu_var1 = pt_to_regu_variable (parser, zero, UNBOX_AS_VALUE);
          regu_var2 = pt_to_regu_variable (parser, node, UNBOX_AS_VALUE);

          pred = pt_make_pred_term_comp (regu_var1, regu_var2, R_NE, data_type);
        }
          else
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
        }
        }
      else
        {
          PT_INTERNAL_ERROR (parser, "pred expr must be logical");
        }
    }
      else
    {
      /* We still need to generate a predicate so that hierarchical queries or aggregate queries with false
       * predicates return the correct answer. */
      PT_NODE *arg1 = parser_new_node (parser, PT_VALUE);
      PT_NODE *arg2 = parser_new_node (parser, PT_VALUE);

      if (arg1 && arg2)
        {
          arg1->type_enum = PT_TYPE_INTEGER;
          if (node->type_enum == PT_TYPE_LOGICAL && node->info.value.data_value.i != 0)
        {
          arg1->info.value.data_value.i = 1;
        }
          else
        {
          arg1->info.value.data_value.i = 0;
        }
          arg2->type_enum = PT_TYPE_INTEGER;
          arg2->info.value.data_value.i = 1;
          data_type = DB_TYPE_INTEGER;

          regu_var1 = pt_to_regu_variable (parser, arg1, UNBOX_AS_VALUE);
          regu_var2 = pt_to_regu_variable (parser, arg2, UNBOX_AS_VALUE);
          pred = pt_make_pred_term_comp (regu_var1, regu_var2, R_EQ, data_type);
        }
      else
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
        }
    }

      node = save_node;     /* restore */
    }

  if (node && pred == NULL)
    {
      if (!pt_has_error (parser))
    {
      PT_INTERNAL_ERROR (parser, "generate predicate");
    }
    }

  return pred;
}

/*
 * pt_to_pred_expr_with_arg () - converts a list of expression tree to
 *  xasl 'pred' expressions, where each item of the list represents
 *  a conjunctive normal form term
 *   return: A NULL return indicates an error occurred
 *   parser(in):
 *   node_list(in):
 *   argp(out):
 */
PRED_EXPR *
pt_to_pred_expr_with_arg (PARSER_CONTEXT * parser, PT_NODE * node_list, int *argp)
{
  PRED_EXPR *cnf_pred, *dnf_pred, *temp;
  PT_NODE *node, *cnf_node, *dnf_node;
  int dummy;
  int num_dnf, i;

  if (!argp)
    {
      argp = &dummy;
    }
  *argp = 0;

  /* convert CNF list into right-linear chains of AND terms */
  cnf_pred = NULL;
  for (node = node_list; node; node = node->next)
    {
      cnf_node = node;

      CAST_POINTER_TO_NODE (cnf_node);

      if (cnf_node->or_next)
    {
      /* if term has OR, set information for inst_num() scan type */
      *argp |= PT_PRED_ARG_INSTNUM_CONTINUE;
      *argp |= PT_PRED_ARG_GRBYNUM_CONTINUE;
      *argp |= PT_PRED_ARG_ORDBYNUM_CONTINUE;
    }


      dnf_pred = NULL;

      num_dnf = 0;
      for (dnf_node = cnf_node; dnf_node; dnf_node = dnf_node->or_next)
    {
      num_dnf++;
    }

      while (num_dnf)
    {
      dnf_node = cnf_node;
      for (i = 1; i < num_dnf; i++)
        {
          dnf_node = dnf_node->or_next;
        }

      /* get the last dnf_node */
      temp = pt_to_pred_expr_local_with_arg (parser, dnf_node, argp);
      if (temp == NULL)
        {
          goto error;
        }

      /* set PT_PRED_ARG_INSTNUM_CONTINUE flag for numbering in each node of the predicate */
      parser_walk_tree (parser, dnf_node, NULL, NULL, pt_numbering_set_continue_post, argp);

      dnf_pred = (dnf_pred) ? pt_make_pred_expr_pred (temp, dnf_pred, B_OR) : temp;

      if (dnf_pred == NULL)
        {
          goto error;
        }

      num_dnf--;        /* decrease to the previous dnf_node */
    }           /* while (num_dnf) */

      cnf_pred = (cnf_pred) ? pt_make_pred_expr_pred (dnf_pred, cnf_pred, B_AND) : dnf_pred;

      if (cnf_pred == NULL)
    {
      goto error;
    }
    }               /* for (node = node_list; ...) */

  return cnf_pred;

error:
  PT_INTERNAL_ERROR (parser, "predicate");
  return NULL;
}

/*
 * pt_to_pred_expr () -
 *   return:
 *   parser(in):
 *   node(in):
 */
PRED_EXPR *
pt_to_pred_expr (PARSER_CONTEXT * parser, PT_NODE * node)
{
  return pt_to_pred_expr_with_arg (parser, node, NULL);
}

#if defined (ENABLE_UNUSED_FUNCTION)
/*
 * look_for_unique_btid () - Search for a UNIQUE constraint B-tree ID
 *   return: 1 on a UNIQUE BTID is found
 *   classop(in): Class object pointer
 *   name(in): Attribute name
 *   btid(in): BTID pointer (BTID is returned)
 */
static int
look_for_unique_btid (DB_OBJECT * classop, const char *name, BTID * btid)
{
  SM_CLASS *class_;
  SM_ATTRIBUTE *att;
  int error = NO_ERROR;
  int ok = 0;

  error = au_fetch_class (classop, &class_, AU_FETCH_READ, AU_SELECT);
  if (error == NO_ERROR)
    {
      att = classobj_find_attribute (class_, name, 0);
      if (att != NULL)
    {
      if (classobj_get_cached_constraint (att->constraints, SM_CONSTRAINT_UNIQUE, btid)
          || classobj_get_cached_constraint (att->constraints, SM_CONSTRAINT_PRIMARY_KEY, btid))
        {
          ok = 1;
        }
    }
    }

  return ok;
}               /* look_for_unique_btid */
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * pt_xasl_type_enum_to_domain () - Given a PT_TYPE_ENUM generate a domain
 *                                  for it and cache it
 *   return:
 *   type(in):
 */
TP_DOMAIN *
pt_xasl_type_enum_to_domain (const PT_TYPE_ENUM type)
{
  TP_DOMAIN *dom;

  dom = pt_type_enum_to_db_domain (type);
  return tp_domain_cache (dom);
}

/*
 * pt_xasl_node_to_domain () - Given a PT_NODE generate a domain
 *                             for it and cache it
 *   return:
 *   parser(in):
 *   node(in):
 */
TP_DOMAIN *
pt_xasl_node_to_domain (PARSER_CONTEXT * parser, const PT_NODE * node)
{
  TP_DOMAIN *dom;

  dom = pt_node_to_db_domain (parser, (PT_NODE *) node, NULL);
  if (dom)
    {
      return tp_domain_cache (dom);
    }
  else
    {
      PT_ERRORc (parser, node, er_msg ());
      return NULL;
    }
}

/*
 * pt_xasl_data_type_to_domain () - Given a PT_DATA_TYPE node generate
 *                                  a domain for it and cache it
 *   return:
 *   parser(in):
 *   node(in):
 */
static TP_DOMAIN *
pt_xasl_data_type_to_domain (PARSER_CONTEXT * parser, const PT_NODE * node)
{
  TP_DOMAIN *dom;

  dom = pt_data_type_to_db_domain (parser, (PT_NODE *) node, NULL);
  return tp_domain_cache (dom);
}

#if defined (ENABLE_UNUSED_FUNCTION)
/*
 * hhhhmmss () - print a time value as 'hhhhmmss'
 *   return:
 *   time(in):
 *   buf(out):
 *   buflen(in):
 */
static int
hhhhmmss (const DB_TIME * time, char *buf, int buflen)
{
  const char date_fmt[] = "00%H%M%S";
  DB_DATE date;

  /* pick any valid date, even though we're interested only in time, to pacify db_strftime */
  db_date_encode (&date, 12, 31, 1970);

  return db_strftime (buf, buflen, date_fmt, &date, (DB_TIME *) time);
}

/*
 * hhmiss () - print a time value as 'hh:mi:ss'
 *   return:
 *   time(in):
 *   buf(out):
 *   buflen(in):
 */
static int
hhmiss (const DB_TIME * time, char *buf, int buflen)
{
  const char date_fmt[] = "%H:%M:%S";
  DB_DATE date;

  /* pick any valid date, even though we're interested only in time, to pacify db_strftime */
  db_date_encode (&date, 12, 31, 1970);

  return db_strftime (buf, buflen, date_fmt, &date, (DB_TIME *) time);
}

/*
 * hhmissms () - print a time value as 'hh:mi:ss.ms'
 *   return:
 *   time(in):
 *   buf(out):
 *   buflen(in):
 */
static int
hhmissms (const unsigned int mtime, char *buf, int buflen)
{
  DB_DATETIME datetime;
  int month, day, year;
  int hour, minute, second, millisecond;
  int retval;

  datetime.date = 0;
  datetime.time = mtime;

  db_datetime_decode (&datetime, &month, &day, &year, &hour, &minute, &second, &millisecond);

  /* "H:%M:%S.MS"; */
  retval = sprintf (buf, "%d:%d:%d.%d", hour, minute, second, millisecond);

  return retval;
}

/*
 * yyyymmdd () - print a date as 'yyyymmdd'
 *   return:
 *   date(in):
 *   buf(out):
 *   buflen(in):
 */
static int
yyyymmdd (const DB_DATE * date, char *buf, int buflen)
{
  const char date_fmt[] = "%Y%m%d";
  DB_TIME time = 0;

  return db_strftime (buf, buflen, date_fmt, (DB_DATE *) date, &time);
}


/*
 * yymmdd () - print a date as 'yyyy-mm-dd'
 *   return:
 *   date(in):
 *   buf(out):
 *   buflen(in):
 */
static int
yymmdd (const DB_DATE * date, char *buf, int buflen)
{
  const char date_fmt[] = "%Y-%m-%d";
  DB_TIME time = 0;

  return db_strftime (buf, buflen, date_fmt, (DB_DATE *) date, &time);
}


/*
 * yymmddhhmiss () - print utime as 'yyyy-mm-dd:hh:mi:ss'
 *   return:
 *   utime(in):
 *   buf(out):
 *   buflen(in):
 */
static int
yymmddhhmiss (const DB_UTIME * utime, char *buf, int buflen)
{
  DB_DATE date;
  DB_TIME time;
  const char fmt[] = "%Y-%m-%d:%H:%M:%S";

  /* extract date & time from utime */
  db_utime_decode (utime, &date, &time);

  return db_strftime (buf, buflen, fmt, &date, &time);
}


/*
 * mmddyyyyhhmiss () - print utime as 'mm/dd/yyyy hh:mi:ss'
 *   return:
 *   utime(in):
 *   buf(in):
 *   buflen(in):
 */
static int
mmddyyyyhhmiss (const DB_UTIME * utime, char *buf, int buflen)
{
  DB_DATE date;
  DB_TIME time;
  const char fmt[] = "%m/%d/%Y %H:%M:%S";

  /* extract date & time from utime */
  db_utime_decode (utime, &date, &time);

  return db_strftime (buf, buflen, fmt, &date, &time);
}

/*
 * yyyymmddhhmissms () - print utime as 'yyyy-mm-dd:hh:mi:ss.ms'
 *   return:
 *   datetime(in):
 *   buf(out):
 *   buflen(in):
 */
static int
yyyymmddhhmissms (const DB_DATETIME * datetime, char *buf, int buflen)
{
  int month, day, year;
  int hour, minute, second, millisecond;
  int retval;

  /* extract date & time from datetime */
  db_datetime_decode (datetime, &month, &day, &year, &hour, &minute, &second, &millisecond);

  /* "%Y-%m-%d:%H:%M:%S.MS"; */
  retval = sprintf (buf, "%d-%d-%d:%d:%d:%d.%d", year, month, day, hour, minute, second, millisecond);

  return retval;
}


/*
 * mmddyyyyhhmissms () - print utime as 'mm/dd/yyyy hh:mi:ss.ms'
 *   return:
 *   datetime(in):
 *   buf(in):
 *   buflen(in):
 */
static int
mmddyyyyhhmissms (const DB_DATETIME * datetime, char *buf, int buflen)
{
  int month, day, year;
  int hour, minute, second, millisecond;
  int retval;

  /* extract date & time from datetime */
  db_datetime_decode (datetime, &month, &day, &year, &hour, &minute, &second, &millisecond);

  /* "%m/%d/%Y %H:%M:%S.MS"; */
  retval = sprintf (buf, "%d/%d/%d %d:%d:%d.%d", month, day, year, hour, minute, second, millisecond);

  return retval;
}
#endif

#if defined (ENABLE_UNUSED_FUNCTION)
/*
 * host_var_name () -  manufacture a host variable name
 *   return:  a host variable name
 *   custom_print(in): a custom_print member
 */
static char *
host_var_name (unsigned int custom_print)
{
  return (char *) "?";
}
#endif

/*
 * pt_table_compatible_node () - Returns compatible if node is non-subquery
 *                               and has matching spec id
 *   return:
 *   parser(in):
 *   tree(in):
 *   void_info(in/out):
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_table_compatible_node (PARSER_CONTEXT * parser, PT_NODE * tree, void *void_info, int *continue_walk)
{
  COMPATIBLE_INFO *info = (COMPATIBLE_INFO *) void_info;

  if (info && tree)
    {
      switch (tree->node_type)
    {
    case PT_SELECT:
    case PT_UNION:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
      info->compatible = NOT_COMPATIBLE;
      *continue_walk = PT_STOP_WALK;
      break;

    case PT_NAME:
      /* check ids match */
      if (tree->info.name.spec_id != info->spec_id)
        {
          info->compatible = NOT_COMPATIBLE;
          *continue_walk = PT_STOP_WALK;
        }
      break;

    case PT_EXPR:
      if (tree->info.expr.op == PT_INST_NUM || tree->info.expr.op == PT_ROWNUM || tree->info.expr.op == PT_LEVEL
          || tree->info.expr.op == PT_CONNECT_BY_ISLEAF || tree->info.expr.op == PT_CONNECT_BY_ISCYCLE
          || tree->info.expr.op == PT_CONNECT_BY_ROOT || tree->info.expr.op == PT_QPRIOR
          || tree->info.expr.op == PT_SYS_CONNECT_BY_PATH)
        {
          info->compatible = NOT_COMPATIBLE;
          *continue_walk = PT_STOP_WALK;
        }
      break;

    default:
      break;
    }
    }

  return tree;
}


/*
 * pt_table_compatible () - Tests the compatibility of the given sql tree
 *                          with a given class specification
 *   return:
 *   parser(in):
 *   node(in):
 *   spec(in):
 */
static int
pt_table_compatible (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * spec)
{
  COMPATIBLE_INFO info;
  info.compatible = ENTITY_COMPATIBLE;

  info.spec_id = spec->info.spec.id;

  parser_walk_tree (parser, node, pt_table_compatible_node, &info, pt_continue_walk, NULL);

  return info.compatible;
}

static PT_NODE *
pt_query_set_reference (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *query, *spec, *temp;

  query = node;
  while (query
     && (query->node_type == PT_UNION || query->node_type == PT_INTERSECTION || query->node_type == PT_DIFFERENCE))
    {
      query = query->info.query.q.union_.arg1;
    }

  if (query)
    {
      spec = query->info.query.q.select.from;
    }
  if (query && spec)
    {
      /* recalculate referenced attributes */
      for (temp = spec; temp; temp = temp->next)
    {
      node = mq_set_references (parser, node, temp);
    }
    }

  return node;
}

/*
 * pt_split_access_if_instnum () - Make a two lists of predicates,
 *       one "simply" compatible with the given table,
 *       one containing any other constructs, one instnum predicates
 *   return:
 *   parser(in):
 *   spec(in):
 *   where(in/out):
 *   access_part(out):
 *   if_part(out):
 *   instnum_part(out):
 */
static void
pt_split_access_if_instnum (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * where, PT_NODE ** access_part,
                PT_NODE ** if_part, PT_NODE ** instnum_part)
{
  PT_NODE *next;
  bool inst_num;

  *access_part = NULL;
  *if_part = NULL;
  *instnum_part = NULL;

  while (where)
    {
      next = where->next;
      where->next = NULL;
      if (pt_table_compatible (parser, where, spec) == ENTITY_COMPATIBLE)
    {
      where->next = *access_part;
      *access_part = where;
    }
      else
    {
      /* check for instnum_predicate */
      inst_num = false;
      (void) parser_walk_tree (parser, where, pt_check_instnum_pre, NULL, pt_check_instnum_post, &inst_num);
      if (inst_num)
        {
          where->next = *instnum_part;
          *instnum_part = where;
        }
      else
        {
          where->next = *if_part;
          *if_part = where;
        }
    }
      where = next;
    }
}

/*
 * pt_split_if_instnum () - Make a two lists of predicates, one containing
 *                          any other constructs (subqueries, other tables, etc.
 *                          except for instnum predicates),
 *                          one instnum predicates.
 *   return:
 *   parser(in):
 *   where(in/out):
 *   if_part(out):
 *   instnum_part(out):
 */
static void
pt_split_if_instnum (PARSER_CONTEXT * parser, PT_NODE * where, PT_NODE ** if_part, PT_NODE ** instnum_part)
{
  PT_NODE *next;
  bool inst_num;

  *if_part = NULL;
  *instnum_part = NULL;

  while (where)
    {
      next = where->next;
      where->next = NULL;

      /* check for instnum_predicate */
      inst_num = false;
      (void) parser_walk_tree (parser, where, pt_check_instnum_pre, NULL, pt_check_instnum_post, &inst_num);
      if (inst_num)
    {
      where->next = *instnum_part;
      *instnum_part = where;
    }
      else
    {
      where->next = *if_part;
      *if_part = where;
    }
      where = next;
    }
}

/*
 * pt_split_having_grbynum () - Make a two lists of predicates, one "simply"
 *      having predicates, and one containing groupby_num() function
 *   return:
 *   parser(in):
 *   having(in/out):
 *   having_part(out):
 *   grbynum_part(out):
 */
static void
pt_split_having_grbynum (PARSER_CONTEXT * parser, PT_NODE * having, PT_NODE ** having_part, PT_NODE ** grbynum_part)
{
  PT_NODE *next;
  bool grbynum_flag;

  *having_part = NULL;
  *grbynum_part = NULL;

  while (having)
    {
      next = having->next;
      having->next = NULL;

      grbynum_flag = false;
      (void) parser_walk_tree (parser, having, pt_check_groupbynum_pre, NULL, pt_check_groupbynum_post, &grbynum_flag);

      if (grbynum_flag)
    {
      having->next = *grbynum_part;
      *grbynum_part = having;
    }
      else
    {
      having->next = *having_part;
      *having_part = having;
    }

      having = next;
    }
}


/*
 * pt_make_identity_offsets () - Create an attr_offset array that
 *                               has 0 for position 0, 1 for position 1, etc
 *   return:
 *   attr_list(in):
 */
static int *
pt_make_identity_offsets (PT_NODE * attr_list)
{
  int *offsets;
  int num_attrs, i;

  num_attrs = pt_length_of_list (attr_list);
  if (num_attrs == 0)
    {
      return NULL;
    }

  offsets = (int *) malloc ((num_attrs + 1) * sizeof (int));
  if (offsets == NULL)
    {
      return NULL;
    }

  for (i = 0; i < num_attrs; i++)
    {
      offsets[i] = i;
    }
  offsets[i] = -1;

  return offsets;
}


/*
 * pt_split_attrs () - Split the attr_list into two lists without destroying
 *      the original list
 *   return:
 *   parser(in):
 *   table_info(in):
 *   pred(in):
 *   pred_attrs(out):
 *   rest_attrs(out):
 *   reserved_attrs(out):
 *   pred_offsets(out):
 *   rest_offsets(out):
 *   reserved_offsets(out):
 *
 * Note :
 * Those attrs that are found in the pred are put on the pred_attrs list,
 * those attrs not found in the pred are put on the rest_attrs list.
 * There are special spec flags that activate reserved attributes, which are
 * handled differently compared with regular attributes.
 * For now only reserved names of record information and page information are
 * used.
 */
static int
pt_split_attrs (PARSER_CONTEXT * parser, TABLE_INFO * table_info, PT_NODE * pred, PT_NODE ** pred_attrs,
        PT_NODE ** rest_attrs, PT_NODE ** reserved_attrs, int **pred_offsets, int **rest_offsets,
        int **reserved_offsets)
{
  PT_NODE *tmp = NULL, *pointer = NULL, *real_attrs = NULL;
  PT_NODE *pred_nodes = NULL;
  int cur_pred, cur_rest, cur_reserved, num_attrs, i;
  PT_NODE *attr_list = NULL;
  PT_NODE *node = NULL, *save_node = NULL, *save_next = NULL;
  PT_NODE *ref_node = NULL;
  bool has_reserved = false;

  pred_nodes = NULL;        /* init */
  *pred_attrs = NULL;
  *rest_attrs = NULL;
  *pred_offsets = NULL;
  *rest_offsets = NULL;
  cur_pred = 0;
  cur_rest = 0;
  if (reserved_attrs != NULL)
    {
      *reserved_attrs = NULL;
    }
  if (reserved_offsets != NULL)
    {
      *reserved_offsets = NULL;
    }
  cur_reserved = 0;

  if (table_info->attribute_list == NULL)
    return NO_ERROR;        /* nothing to do */

  num_attrs = pt_length_of_list (table_info->attribute_list);
  attr_list = table_info->attribute_list;

  has_reserved = PT_SHOULD_BIND_RESERVED_NAME (table_info->class_spec);
  if (has_reserved)
    {
      assert (reserved_attrs != NULL);
      assert (reserved_offsets != NULL);
      *reserved_offsets = (int *) malloc (num_attrs * sizeof (int));
      if (*reserved_offsets == NULL)
    {
      goto exit_on_error;
    }
    }

  if ((*pred_offsets = (int *) malloc (num_attrs * sizeof (int))) == NULL)
    {
      goto exit_on_error;
    }

  if ((*rest_offsets = (int *) malloc (num_attrs * sizeof (int))) == NULL)
    {
      goto exit_on_error;
    }

  if (pred)
    {
      /* mq_get_references() is destructive to the real set of referenced attrs, so we need to squirrel it away. */
      real_attrs = table_info->class_spec->info.spec.referenced_attrs;
      table_info->class_spec->info.spec.referenced_attrs = NULL;

      /* Traverse pred */
      for (node = pred; node; node = node->next)
    {
      save_node = node; /* save */

      CAST_POINTER_TO_NODE (node);

      if (node)
        {
          /* save and cut-off node link */
          save_next = node->next;
          node->next = NULL;

          ref_node = mq_get_references_helper (parser, node, table_info->class_spec, false);
          pred_nodes = parser_append_node (ref_node, pred_nodes);

          /* restore node link */
          node->next = save_next;
        }

      node = save_node; /* restore */
    }           /* for (node = ...) */

      table_info->class_spec->info.spec.referenced_attrs = real_attrs;
    }

  tmp = attr_list;
  i = 0;
  while (tmp)
    {
      if (has_reserved && tmp->node_type == PT_NAME && tmp->info.name.meta_class == PT_RESERVED)
    {
      /* add to reserved */
      pointer = pt_point (parser, tmp);
      if (pointer == NULL)
        {
          goto exit_on_error;
        }
      *reserved_attrs = parser_append_node (pointer, *reserved_attrs);
      (*reserved_offsets)[cur_reserved++] = i;
      tmp = tmp->next;
      i++;
      continue;
    }

      pointer = pt_point (parser, tmp);
      if (pointer == NULL)
    {
      goto exit_on_error;
    }

      if (pt_find_attribute (parser, tmp, pred_nodes) != -1)
    {
      *pred_attrs = parser_append_node (pointer, *pred_attrs);
      (*pred_offsets)[cur_pred++] = i;
    }
      else
    {
      *rest_attrs = parser_append_node (pointer, *rest_attrs);
      (*rest_offsets)[cur_rest++] = i;
    }
      tmp = tmp->next;
      i++;
    }

  if (pred_nodes)
    {
      parser_free_tree (parser, pred_nodes);
    }

  return NO_ERROR;

exit_on_error:

  parser_free_tree (parser, *pred_attrs);
  parser_free_tree (parser, *rest_attrs);
  if (reserved_attrs != NULL)
    {
      parser_free_tree (parser, *reserved_attrs);
    }
  if (*pred_offsets != NULL)
    {
      free_and_init (*pred_offsets);
    }
  if (*rest_offsets != NULL)
    {
      free_and_init (*rest_offsets);
    }
  if (reserved_offsets != NULL && *reserved_offsets != NULL)
    {
      free_and_init (*reserved_offsets);
    }
  if (pred_nodes)
    {
      parser_free_tree (parser, pred_nodes);
    }

  return ER_FAILED;
}

/*
 * pt_split_hash_attrs () - Split the attr_list into two lists without destroying
 *      the original list
 *   return:
 *   parser(in):
 *   table_info(in):
 *   pred(in):
 *   build_attrs(out):
 *   probe_attrs(out):
 *
 * Note :
 */
static int
pt_split_hash_attrs (PARSER_CONTEXT * parser, TABLE_INFO * table_info, PT_NODE * pred, PT_NODE ** build_attrs,
             PT_NODE ** probe_attrs)
{
  PT_NODE *node = NULL, *save_node = NULL, *save_next = NULL;
  PT_NODE *arg1 = NULL, *arg2 = NULL;

  assert (build_attrs != NULL && *build_attrs == NULL);
  assert (probe_attrs != NULL && *probe_attrs == NULL);
  *build_attrs = NULL;
  *probe_attrs = NULL;

  if (table_info->attribute_list == NULL)
    {
      return NO_ERROR;      /* nothing to do */
    }

  if (pred)
    {
      /* Traverse pred */
      for (node = pred; node; node = node->next)
    {
      save_node = node; /* save */

      CAST_POINTER_TO_NODE (node);

      if (!pt_is_expr_node (node))
        {
          continue;
        }
      else
        {
          /* save and cut-off node link */
          save_next = node->next;
          node->next = NULL;

          arg1 = node->info.expr.arg1;
          arg2 = node->info.expr.arg2;
          assert (arg1 != NULL && arg2 != NULL);

          UINTPTR spec_id[2], spec_id2[2];
          spec_id[0] = spec_id2[0] = table_info->spec_id;
          spec_id[1] = spec_id2[1] = 0;
          parser_walk_tree (parser, arg1, pt_is_spec_node, &spec_id, NULL, NULL);
          parser_walk_tree (parser, arg2, pt_is_spec_node, &spec_id2, NULL, NULL);

          if (spec_id[1] == spec_id2[1])
        {
          continue;
        }

          if (spec_id[1])
        {
          /* arg1 is current spec */
          *build_attrs = parser_append_node (parser_copy_tree (parser, arg1), *build_attrs);
          *probe_attrs = parser_append_node (parser_copy_tree (parser, arg2), *probe_attrs);
        }
          else
        {
          /* arg2 is current spec */
          *build_attrs = parser_append_node (parser_copy_tree (parser, arg2), *build_attrs);
          *probe_attrs = parser_append_node (parser_copy_tree (parser, arg1), *probe_attrs);
        }

          /* restore node link */
          node->next = save_next;
        }

      node = save_node; /* restore */
    }           /* for (node = ...) */
    }

  return NO_ERROR;

exit_on_error:

  parser_free_tree (parser, *probe_attrs);
  parser_free_tree (parser, *build_attrs);

  return ER_FAILED;
}

/*
 * pt_split_pred_for_HQ () - Split the predicate into two lists without destroying
 *      the original list for HQ
 *   return:
 *   parser(in):
 *   pred(in):
 *   pred_without_HQ(out):
 *   pred_with_HQ(out):
 */
static int
pt_split_pred_for_HQ (PARSER_CONTEXT * parser, PT_NODE * pred, PT_NODE ** pred_without_HQ, PT_NODE ** pred_with_HQ)
{
  PT_NODE *node = NULL, *save_next = NULL;
  bool is_hierarchical_op;

  if (pred)
    {
      /* Traverse pred */
      for (node = pred; node; node = node->next)
    {
      /* save and cut-off node link */
      save_next = node->next;
      node->next = NULL;

      /* find Reserved words for HQ */
      is_hierarchical_op = false;
      parser_walk_tree (parser, node, pt_find_hq_op_except_prior, &is_hierarchical_op, NULL, NULL);;

      if (!is_hierarchical_op)
        {
          *pred_without_HQ = parser_append_node (parser_copy_tree (parser, node), *pred_without_HQ);
        }
      else
        {
          *pred_with_HQ = parser_append_node (parser_copy_tree (parser, node), *pred_with_HQ);
        }

      /* restore node link */
      node->next = save_next;
    }
    }

  return NO_ERROR;
}

/*
 * pt_split_hash_attrs_for_HQ () - Split the attr_list into two lists without destroying
 *      the original list for HQ
 *   return:
 *   parser(in):
 *   pred(in):
 *   build_attrs(out):
 *   probe_attrs(out):
 *
 * Note :
 * is_PRIOR | NAME_without_prior | characteristic
 *    O     |        O           | unhashable
 *    O     |        X           | probe attr
 *    X     |        O           | build attr
 *    X     |        X           | constant (can be probe or build attr)
 */
static int
pt_split_hash_attrs_for_HQ (PARSER_CONTEXT * parser, PT_NODE * pred, PT_NODE ** build_attrs, PT_NODE ** probe_attrs,
                PT_NODE ** pred_without_HQ)
{
  PT_NODE *node = NULL, *save_node = NULL, *save_next = NULL;
  PT_NODE *arg1 = NULL, *arg2 = NULL;

  assert (build_attrs != NULL && *build_attrs == NULL);
  assert (probe_attrs != NULL && *probe_attrs == NULL);
  *build_attrs = NULL;
  *probe_attrs = NULL;
  bool is_hierarchical_op;

  if (pred)
    {
      /* Traverse pred */
      for (node = pred; node; node = node->next)
    {
      /* save and cut-off node link */
      save_next = node->next;
      node->next = NULL;

      /* find Reserved words for HQ */
      is_hierarchical_op = false;
      parser_walk_tree (parser, node, pt_find_hq_op_except_prior, &is_hierarchical_op, NULL, NULL);;

      /* Predicates containing HQ are not hashable because they have to be evaluated in the HQ proc. */
      /* Reserved words for HQ is not allowed (LEVEL, CONNECT_BY_ISLEAF....) */
      if (!is_hierarchical_op)
        {
          *pred_without_HQ = parser_append_node (parser_copy_tree (parser, node), *pred_without_HQ);
        }

      /* restore node link */
      node->next = save_next;
    }
    }

  if (*pred_without_HQ)
    {
      /* Traverse pred */
      for (node = *pred_without_HQ; node; node = node->next)
    {
      save_node = node; /* save */

      CAST_POINTER_TO_NODE (node);

      if (!PT_IS_EXPR_NODE_WITH_OPERATOR (node, PT_EQ) || node->or_next)
        {
          /* HASH LIST SCAN for HQ is possible under the following conditions */
          /* 1. CNF predicate (node is NOT PT_AND, PT_OR) */
          /* 2. only equal operation */
          /* 3. predicate without OR (or_next is null) */
          /* 4. symmetric predicate (having PRIOR, probe. having NAME, build. Having these two makes it unhashable) */
          /* 5. subquery is not allowed in syntax check */
          /* 6. Reserved words for HQ is not allowed (LEVEL, CONNECT_BY_ISLEAF....)  */
          continue;
        }
      else
        {
          /* save and cut-off node link */
          save_next = node->next;
          node->next = NULL;

          arg1 = node->info.expr.arg1;
          arg2 = node->info.expr.arg2;
          assert (arg1 != NULL && arg2 != NULL);

          // *INDENT-OFF*
          HASHABLE hashable_arg1, hashable_arg2;
          hashable_arg1 = hashable_arg2 = {false, false};
          HASH_ATTR hash_arg1, hash_arg2;
          // *INDENT-ON*

          parser_walk_tree (parser, arg1, pt_check_hashable, &hashable_arg1, NULL, NULL);
          parser_walk_tree (parser, arg2, pt_check_hashable, &hashable_arg2, NULL, NULL);

          CHECK_HASH_ATTR (hashable_arg1, hash_arg1);
          CHECK_HASH_ATTR (hashable_arg2, hash_arg2);

          if ((hash_arg1 == PROBE && hash_arg2 == BUILD) ||
          (hash_arg1 == PROBE && hash_arg2 == CONSTANT) || (hash_arg1 == CONSTANT && hash_arg2 == BUILD))
        {
          /* arg1 is probe attr and arg2 is build attr */
          *build_attrs = parser_append_node (parser_copy_tree (parser, arg2), *build_attrs);
          *probe_attrs = parser_append_node (parser_copy_tree (parser, arg1), *probe_attrs);
        }
          else if ((hash_arg1 == BUILD && hash_arg2 == PROBE) ||
               (hash_arg1 == BUILD && hash_arg2 == CONSTANT) || (hash_arg1 == CONSTANT && hash_arg2 == PROBE))
        {
          /* arg1 is build attr and arg2 is probe attr */
          *build_attrs = parser_append_node (parser_copy_tree (parser, arg1), *build_attrs);
          *probe_attrs = parser_append_node (parser_copy_tree (parser, arg2), *probe_attrs);
        }
          else
        {
          /* unhashable predicate */
        }

          /* restore node link */
          node->next = save_next;
        }

      node = save_node; /* restore */
    }           /* for (node = ...) */
    }

  return NO_ERROR;

exit_on_error:

  parser_free_tree (parser, *probe_attrs);
  parser_free_tree (parser, *build_attrs);
  parser_free_tree (parser, *pred_without_HQ);

  return ER_FAILED;
}

/*
 * pt_to_index_attrs () - Those attrs that are found in the key-range pred
 *                        and key-filter pred are put on the pred_attrs list
 *   return:
 *   parser(in):
 *   table_info(in):
 *   index_pred(in):
 *   key_filter_pred(in):
 *   pred_attrs(out):
 *   pred_offsets(out):
 */
static int
pt_to_index_attrs (PARSER_CONTEXT * parser, TABLE_INFO * table_info, QO_XASL_INDEX_INFO * index_pred,
           PT_NODE * key_filter_pred, PT_NODE ** pred_attrs, int **pred_offsets)
{
  PT_NODE *tmp, *pointer, *real_attrs;
  PT_NODE *pred_nodes;
  int cur_pred, num_attrs, i;
  PT_NODE *attr_list = table_info->attribute_list;
  PT_NODE **term_exprs;
  int nterms;
  PT_NODE *node, *save_node, *save_next, *ref_node;

  pred_nodes = NULL;        /* init */
  *pred_attrs = NULL;
  *pred_offsets = NULL;
  cur_pred = 0;

  if (!attr_list)
    return 1;           /* nothing to do */

  num_attrs = pt_length_of_list (attr_list);
  *pred_offsets = (int *) malloc (num_attrs * sizeof (int));
  if (*pred_offsets == NULL)
    {
      goto exit_on_error;
    }

  /* mq_get_references() is destructive to the real set of referenced attrs, so we need to squirrel it away. */
  real_attrs = table_info->class_spec->info.spec.referenced_attrs;
  table_info->class_spec->info.spec.referenced_attrs = NULL;

  if (prm_get_bool_value (PRM_ID_ORACLE_STYLE_EMPTY_STRING))
    {
      nterms = qo_xasl_get_num_terms (index_pred);
      term_exprs = qo_xasl_get_terms (index_pred);

      /* Traverse key-range pred */
      for (i = 0; i < nterms; i++)
    {
      save_node = node = term_exprs[i];

      CAST_POINTER_TO_NODE (node);

      if (node)
        {
          /* save and cut-off node link */
          save_next = node->next;
          node->next = NULL;

          /* exclude path entities */
          ref_node = mq_get_references_helper (parser, node, table_info->class_spec, false);

          assert (ref_node != NULL);

          /* need to check zero-length empty string */
          if (ref_node != NULL && (ref_node->type_enum == PT_TYPE_VARCHAR || ref_node->type_enum == PT_TYPE_VARBIT))
        {
          pred_nodes = parser_append_node (ref_node, pred_nodes);
        }

          /* restore node link */
          node->next = save_next;
        }

      term_exprs[i] = save_node;    /* restore */
    }           /* for (i = 0; ...) */
    }

  /* Traverse key-filter pred */
  for (node = key_filter_pred; node; node = node->next)
    {
      save_node = node;     /* save */

      CAST_POINTER_TO_NODE (node);

      if (node)
    {
      /* save and cut-off node link */
      save_next = node->next;
      node->next = NULL;

      /* exclude path entities */
      ref_node = mq_get_references_helper (parser, node, table_info->class_spec, false);
      pred_nodes = parser_append_node (ref_node, pred_nodes);

      /* restore node link */
      node->next = save_next;
    }

      node = save_node;     /* restore */
    }               /* for (node = ...) */

  table_info->class_spec->info.spec.referenced_attrs = real_attrs;

  if (!pred_nodes)      /* there is not key-filter pred */
    {
      return 1;
    }

  tmp = attr_list;
  i = 0;
  while (tmp)
    {
      if (pt_find_attribute (parser, tmp, pred_nodes) != -1)
    {
      if ((pointer = pt_point (parser, tmp)) == NULL)
        {
          goto exit_on_error;
        }
      *pred_attrs = parser_append_node (pointer, *pred_attrs);
      (*pred_offsets)[cur_pred++] = i;
    }
      tmp = tmp->next;
      i++;
    }

  if (pred_nodes)
    {
      parser_free_tree (parser, pred_nodes);
    }

  return 1;

exit_on_error:

  parser_free_tree (parser, *pred_attrs);
  free_and_init (*pred_offsets);
  if (pred_nodes)
    {
      parser_free_tree (parser, pred_nodes);
    }
  return 0;
}


/*
 * pt_flush_classes () - Flushes each class encountered
 *   return:
 *   parser(in):
 *   node(in):
 *   void_arg(in):
 *   continue_walk(in/out):
 */
PT_NODE *
pt_flush_classes (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE *class_;
  int isvirt;
  MOP clsmop = NULL;
  SM_CLASS *smcls = NULL;

  /* If parser->dont_flush is asserted, skip the flushing. */
  if (node->node_type == PT_SPEC)
    {
      for (class_ = node->info.spec.flat_entity_list; class_; class_ = class_->next)
    {
      clsmop = class_->info.name.db_object;
      if (clsmop == NULL)
        {
          assert (false);
          PT_ERROR (parser, node, "Generic error");
        }
      /* if class object is not dirty and doesn't contain any dirty instances, do not flush the class and its
       * instances */
      if (WS_ISDIRTY (class_->info.name.db_object) || ws_has_dirty_objects (class_->info.name.db_object, &isvirt))
        {
          if (sm_flush_objects (class_->info.name.db_object) != NO_ERROR)
        {
          PT_ERRORc (parser, class_, er_msg ());
        }
        }
      /* Also test if we need to flush partitions of each class */
      if (locator_is_class (clsmop, DB_FETCH_READ) <= 0)
        {
          continue;
        }
      if (au_fetch_class_force (clsmop, &smcls, AU_FETCH_READ) != NO_ERROR)
        {
          PT_ERRORc (parser, class_, er_msg ());
        }
      if (smcls != NULL && smcls->partition != NULL)
        {
          /* flush all partitions */
          DB_OBJLIST *user = NULL;

          for (user = smcls->users; user != NULL; user = user->next)
        {
          if (WS_ISDIRTY (user->op) || ws_has_dirty_objects (user->op, &isvirt))
            {
              if (sm_flush_objects (user->op) != NO_ERROR)
            {
              PT_ERRORc (parser, class_, er_msg ());
            }
            }
        }
        }
    }
    }

  return node;
}

/*
 * pt_set_is_system_generated_stmt () -
 *   return:
 *   parser(in):
 *   tree(in):
 *   void_arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
pt_set_is_system_generated_stmt (PARSER_CONTEXT * parser, PT_NODE * tree, void *void_arg, int *continue_walk)
{
  if (PT_IS_QUERY_NODE_TYPE (tree->node_type))
    {
      bool is_system_generated_stmt;

      is_system_generated_stmt = *(bool *) void_arg;
      tree->flag.is_system_generated_stmt = is_system_generated_stmt;
    }

  return tree;
}

/*
 * pt_flush_class_and_null_xasl () - Flushes each class encountered
 *  Partition pruning is applied to PT_SELECT nodes
 *   return:
 *   parser(in):
 *   tree(in):
 *   void_arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
pt_flush_class_and_null_xasl (PARSER_CONTEXT * parser, PT_NODE * tree, void *void_arg, int *continue_walk)
{
  if (ws_has_updated ())
    {
      tree = pt_flush_classes (parser, tree, void_arg, continue_walk);
    }

  if (PT_IS_QUERY_NODE_TYPE (tree->node_type))
    {
      tree->info.query.xasl = NULL;
    }
  else if (tree->node_type == PT_DATA_TYPE)
    {
      PT_NODE *entity;

      /* guard against proxies & views not correctly tagged in data type nodes */
      entity = tree->info.data_type.entity;
      if (entity)
    {
      if (entity->info.name.meta_class != PT_META_CLASS && db_is_vclass (entity->info.name.db_object) > 0
          && !tree->info.data_type.virt_object)
        {
          tree->info.data_type.virt_object = entity->info.name.db_object;
        }
    }
    }

  return tree;
}

/*
 * pt_null_xasl () - Set all the query node's xasl to NULL
 *   return:
 *   parser(in):
 *   tree(in):
 *   void_arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
pt_null_xasl (PARSER_CONTEXT * parser, PT_NODE * tree, void *void_arg, int *continue_walk)
{
  if (PT_IS_QUERY_NODE_TYPE (tree->node_type))
    {
      tree->info.query.xasl = NULL;
    }

  return tree;
}

/*
 * pt_is_spec_node () - return node with the same spec id
 *   return:
 *   parser(in):
 *   tree(in):
 *   void_arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
pt_is_spec_node (PARSER_CONTEXT * parser, PT_NODE * tree, void *void_arg, int *continue_walk)
{
  if (pt_is_name_node (tree))
    {
      UINTPTR *spec_id = (UINTPTR *) void_arg;
      if (tree->info.name.spec_id == spec_id[0])
    {
      *continue_walk = PT_STOP_WALK;
      spec_id[1] = 1;
    }
    }
  return tree;
}

/*
 * pt_check_hashable () - check whether hashable or not
 *   return:
 *   parser(in):
 *   tree(in):
 *   void_arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
pt_check_hashable (PARSER_CONTEXT * parser, PT_NODE * tree, void *void_arg, int *continue_walk)
{
  *continue_walk = PT_CONTINUE_WALK;
  HASHABLE *hashable = (HASHABLE *) void_arg;

  if (PT_IS_EXPR_NODE_WITH_OPERATOR (tree, PT_PRIOR))
    {
      hashable->is_PRIOR = true;
      *continue_walk = PT_LIST_WALK;
    }
  else if (pt_is_name_node (tree))
    {
      hashable->is_NAME_without_prior = true;
    }

  return tree;
}

/*
 * pt_find_hq_op_except_prior() - Check expression tree for hierarchical op except PRIOR
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in/out):
 *   continue_walk(in):
 */
static PT_NODE *
pt_find_hq_op_except_prior (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  bool *is_hierarchical_op = (bool *) arg;

  if (node->node_type == PT_EXPR && PT_CHECK_HQ_OP_EXCEPT_PRIOR (node->info.expr.op))
    {
      *is_hierarchical_op = true;
      *continue_walk = PT_STOP_WALK;
    }

  return node;
}

/*
 * pt_is_subquery () -
 *   return: true if symbols comes from a subquery of a UNION-type thing
 *   node(in):
 */
static int
pt_is_subquery (PT_NODE * node)
{
  PT_MISC_TYPE subquery_type = node->info.query.is_subquery;

  return (subquery_type != 0);
}

/*
 * pt_table_info_alloc () - Allocates and inits an TABLE_INFO structure
 *                      from temporary memory
 *   return:
 *   pt_table_info_alloc(in):
 */
static TABLE_INFO *
pt_table_info_alloc (void)
{
  TABLE_INFO *table_info;

  table_info = (TABLE_INFO *) pt_alloc_packing_buf (sizeof (TABLE_INFO));

  if (table_info)
    {
      table_info->next = NULL;
      table_info->class_spec = NULL;
      table_info->exposed = NULL;
      table_info->spec_id = 0;
      table_info->attribute_list = NULL;
      table_info->value_list = NULL;
      table_info->is_fetch = 0;
    }

  return table_info;
}

/*
 * pt_symbol_info_alloc () - Allocates and inits an SYMBOL_INFO structure
 *                           from temporary memory
 *   return:
 */
static SYMBOL_INFO *
pt_symbol_info_alloc (void)
{
  SYMBOL_INFO *symbols;

  symbols = (SYMBOL_INFO *) pt_alloc_packing_buf (sizeof (SYMBOL_INFO));

  if (symbols)
    {
      symbols->stack = NULL;
      symbols->table_info = NULL;
      symbols->current_class = NULL;
      symbols->cache_attrinfo = NULL;
      symbols->current_listfile = NULL;
      symbols->listfile_unbox = UNBOX_AS_VALUE;
      symbols->listfile_value_list = NULL;
      symbols->reserved_values = NULL;

      /* only used for server inserts and updates */
      symbols->listfile_attr_offset = 0;

      symbols->query_node = NULL;
    }

  return symbols;
}


/*
 * pt_is_single_tuple () -
 *   return: true if select can be determined to return exactly one tuple
 *           This means an aggregate function was used with no group_by clause
 *   parser(in):
 *   select_node(in):
 */
int
pt_is_single_tuple (PARSER_CONTEXT * parser, PT_NODE * select_node)
{
  if (select_node->info.query.q.select.group_by != NULL)
    {
      return false;
    }

  return pt_has_aggregate (parser, select_node);
}


/*
 * pt_filter_pseudo_specs () - Returns list of specs to participate
 *                             in a join cross product
 *   return:
 *   parser(in):
 *   spec(in/out):
 */
static PT_NODE *
pt_filter_pseudo_specs (PARSER_CONTEXT * parser, PT_NODE * spec)
{
  PT_NODE **last, *temp1, *temp2;
  PT_FLAT_SPEC_INFO info;

  if (spec)
    {
      last = &spec;
      temp2 = *last;
      while (temp2)
    {
      if ((temp1 = temp2->info.spec.derived_table) && temp1->node_type == PT_VALUE
          && temp1->type_enum == PT_TYPE_NULL)
        {
          /* fix this derived table so that it is generatable */
          temp1->type_enum = PT_TYPE_SET;
          temp1->info.value.db_value_is_initialized = 0;
          temp1->info.value.data_value.set = NULL;
          temp2->info.spec.derived_table_type = PT_IS_SET_EXPR;
        }

      if (temp2->info.spec.derived_table_type == PT_IS_WHACKED_SPEC)
        {
          /* remove it */
          *last = temp2->next;
        }
      else
        {
          /* keep it */
          last = &temp2->next;
        }
      temp2 = *last;
    }
    }

  if (!spec)
    {
      spec = parser_new_node (parser, PT_SPEC);
      if (spec == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

      spec->info.spec.id = (UINTPTR) spec;
      spec->info.spec.only_all = PT_ONLY;
      spec->info.spec.meta_class = PT_CLASS;
      spec->info.spec.entity_name = pt_name (parser, "dual");
      if (spec->info.spec.entity_name == NULL)
    {
      parser_free_node (parser, spec);
      return NULL;
    }

      info.spec_parent = NULL;
      info.for_update = false;
      spec = parser_walk_tree (parser, spec, pt_flat_spec_pre, &info, pt_continue_walk, NULL);
    }
  return spec;
}

/*
 * pt_make_val_list () - Makes a val list with a DB_VALUE place holder
 *                       for every attribute on an attribute list
 *   return:
 *   attribute_list(in):
 */
static VAL_LIST *
pt_make_val_list (PARSER_CONTEXT * parser, PT_NODE * attribute_list)
{
  VAL_LIST *value_list = NULL;
  QPROC_DB_VALUE_LIST dbval_list;
  QPROC_DB_VALUE_LIST *dbval_list_tail;
  PT_NODE *attribute;

  regu_alloc (value_list);
  if (value_list == NULL)
    {
      return NULL;
    }

  value_list->val_cnt = 0;
  value_list->valp = NULL;
  dbval_list_tail = &value_list->valp;

  for (attribute = attribute_list; attribute != NULL; attribute = attribute->next)
    {
      // init regu
      regu_alloc (dbval_list);
      regu_alloc (dbval_list->val);
      // init value with expected type
      pt_data_type_init_value (attribute, dbval_list->val);
      dbval_list->dom = pt_xasl_node_to_domain (parser, attribute);

      value_list->val_cnt++;
      (*dbval_list_tail) = dbval_list;
      dbval_list_tail = &dbval_list->next;
      dbval_list->next = NULL;
    }

  return value_list;
}


/*
 * pt_clone_val_list () - Makes a val list with a DB_VALUE place holder
 *                        for every attribute on an attribute list
 *   return:
 *   parser(in):
 *   attribute_list(in):
 */
static VAL_LIST *
pt_clone_val_list (PARSER_CONTEXT * parser, PT_NODE * attribute_list)
{
  VAL_LIST *value_list = NULL;
  QPROC_DB_VALUE_LIST dbval_list;
  QPROC_DB_VALUE_LIST *dbval_list_tail;
  PT_NODE *attribute;
  REGU_VARIABLE *regu = NULL;

  regu_alloc (value_list);
  if (value_list == NULL)
    {
      return NULL;
    }

  value_list->val_cnt = 0;
  value_list->valp = NULL;
  dbval_list_tail = &value_list->valp;

  for (attribute = attribute_list; attribute != NULL; attribute = attribute->next)
    {
      regu_alloc (dbval_list);
      regu = pt_attribute_to_regu (parser, attribute);
      if (dbval_list && regu)
    {
      dbval_list->val = pt_regu_to_dbvalue (parser, regu);
      dbval_list->dom = regu->domain;

      value_list->val_cnt++;
      (*dbval_list_tail) = dbval_list;
      dbval_list_tail = &dbval_list->next;
      dbval_list->next = NULL;
    }
      else
    {
      return NULL;
    }
    }

  return value_list;
}


/*
 * pt_find_table_info () - Finds the table_info associated with an exposed name
 *   return:
 *   spec_id(in):
 *   exposed_list(in):
 */
static TABLE_INFO *
pt_find_table_info (UINTPTR spec_id, TABLE_INFO * exposed_list)
{
  TABLE_INFO *table_info;

  table_info = exposed_list;

  /* look down list until name matches, or NULL reached */
  while (table_info && table_info->spec_id != spec_id)
    {
      table_info = table_info->next;
    }

  return table_info;
}

/*
 * pt_is_hash_agg_eligible () - determine if query is eligible for hash
 *                              aggregate evaluation
 *   return: tree node
 *   parser(in): parser context
 *   tree(in): tree node to check for eligibility
 *   arg(in/out): pointer to int, eligibility
 *   continue_walk(in/out): continue walk
 */
static PT_NODE *
pt_is_hash_agg_eligible (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
  int *eligible = (int *) arg;

  if (tree && eligible && pt_is_aggregate_function (parser, tree))
    {
      if (tree->info.function.function_type == PT_GROUP_CONCAT || tree->info.function.function_type == PT_MEDIAN
      || tree->info.function.function_type == PT_CUME_DIST || tree->info.function.function_type == PT_PERCENT_RANK
      || tree->info.function.all_or_distinct == PT_DISTINCT
      || tree->info.function.function_type == PT_PERCENTILE_CONT
      || tree->info.function.function_type == PT_PERCENTILE_DISC)
    {
      *eligible = 0;
      *continue_walk = PT_STOP_WALK;
    }
    }

  return tree;
}

/*
 * pt_to_aggregate_node () - test for aggregate function nodes,
 *                       convert them to aggregate_list_nodes
 *   return:
 *   parser(in):
 *   tree(in):
 *   arg(in/out):
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_to_aggregate_node (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
  bool is_agg = 0;
  REGU_VARIABLE *regu = NULL;
  REGU_VARIABLE *percentile_regu = NULL;
  AGGREGATE_TYPE *aggregate_list;
  AGGREGATE_INFO *info = (AGGREGATE_INFO *) arg;
  VAL_LIST *value_list;
  MOP classop;
  PT_NODE *group_concat_sep_node_save = NULL;
  PT_NODE *pointer = NULL;
  PT_NODE *pt_val = NULL;
  PT_NODE *percentile = NULL;

  // it contains a list of positions
  REGU_VARIABLE_LIST regu_position_list = NULL;
  // it contains a list of constants, which will be used for the operands
  REGU_VARIABLE_LIST regu_constant_list = NULL;

  REGU_VARIABLE_LIST scan_regu_constant_list = NULL;
  int error_code = NO_ERROR;

  *continue_walk = PT_CONTINUE_WALK;

  is_agg = pt_is_aggregate_function (parser, tree);
  if (is_agg)
    {
      FUNC_CODE code = tree->info.function.function_type;

      if (code == PT_GROUPBY_NUM)
    {
      regu_alloc (aggregate_list);
      if (aggregate_list == NULL)
        {
          PT_ERROR (parser, tree,
            msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC,
                    MSGCAT_SEMANTIC_OUT_OF_MEMORY));
          return tree;
        }
      aggregate_list->next = info->head_list;
      aggregate_list->option = Q_ALL;
      aggregate_list->domain = &tp_Integer_domain;
      if (info->grbynum_valp)
        {
          if (!(*(info->grbynum_valp)))
        {
          regu_alloc (*(info->grbynum_valp));
          regu_dbval_type_init (*(info->grbynum_valp), DB_TYPE_INTEGER);
        }
          aggregate_list->accumulator.value = *(info->grbynum_valp);
        }
      aggregate_list->function = code;
      aggregate_list->opr_dbtype = DB_TYPE_NULL;
    }
      else
    {
      regu_alloc (aggregate_list);
      if (aggregate_list == NULL)
        {
          PT_ERROR (parser, tree, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC,
                              MSGCAT_SEMANTIC_OUT_OF_MEMORY));
          return tree;
        }
      if (aggregate_list->accumulator.value == NULL || aggregate_list->accumulator.value2 == NULL
          || aggregate_list->list_id == NULL)
        {
          PT_ERROR (parser, tree, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC,
                              MSGCAT_SEMANTIC_OUT_OF_MEMORY));
          return tree;
        }
      aggregate_list->next = info->head_list;
      aggregate_list->option = (tree->info.function.all_or_distinct == PT_ALL) ? Q_ALL : Q_DISTINCT;
      aggregate_list->function = code;
      /* others will be set after resolving arg_list */
    }

      aggregate_list->flag.agg_optimized = false;
      BTID_SET_NULL (&aggregate_list->btid);
      aggregate_list->flag.min_max_optimized = false;
      if (info->flag_agg_optimize
      && (aggregate_list->function == PT_COUNT_STAR
          || aggregate_list->function == PT_MAX || aggregate_list->function == PT_MIN))
    {
      BTID *btid = NULL;
      bool need_unique_index;

      classop = sm_find_class (info->class_name);
      if (aggregate_list->function == PT_COUNT_STAR)
        {
          need_unique_index = true;
        }
      else
        {
          need_unique_index = false;
        }

      /* enable count optimization in MVCC if have unique index */
      if (aggregate_list->function == PT_COUNT_STAR)
        {
          btid = sm_find_index (classop, NULL, 0, need_unique_index, false, &aggregate_list->btid);
          if (btid != NULL)
        {
          /* If btree does not exist, optimize with heap in non-MVCC */
          aggregate_list->flag.agg_optimized = true;
        }
        }
      else if (tree->info.function.arg_list->node_type == PT_NAME)
        {
          /* need to get an index has the argument name as first attribute */
          /* no prefix, no filter, no function */
          btid = sm_find_index (classop, (char **) &tree->info.function.arg_list->info.name.original,
                    1, need_unique_index, true, &aggregate_list->btid);
          if (btid != NULL)
        {
          /* If btree does not exist, optimize with heap in non-MVCC */
          aggregate_list->flag.agg_optimized = true;
        }
        }
    }
      else if ((aggregate_list->function == PT_MIN || aggregate_list->function == PT_MAX)
           && info->flag_agg_min_max_optimized)
    {
      pt_optimize_min_max_list (parser, tree, info->qo_plan, aggregate_list);
    }

      if (aggregate_list->function != PT_COUNT_STAR && aggregate_list->function != PT_GROUPBY_NUM)
    {
      if (aggregate_list->function != PT_CUME_DIST && aggregate_list->function != PT_PERCENT_RANK)
        {
          regu_constant_list = pt_to_regu_variable_list (parser, tree->info.function.arg_list, UNBOX_AS_VALUE,
                                 NULL, NULL);

          scan_regu_constant_list = pt_to_regu_variable_list (parser, tree->info.function.arg_list, UNBOX_AS_VALUE,
                                  NULL, NULL);

          if (!regu_constant_list || !scan_regu_constant_list)
        {
          return NULL;
        }
        }
      else
        {
          /* for CUME_DIST and PERCENT_RANK function, take sort list as variables as well */
          regu = pt_to_cume_dist_percent_rank_regu_variable (parser, tree, UNBOX_AS_VALUE);
          if (!regu)
        {
          return NULL;
        }

          REGU_VARIABLE_LIST to_add;
          regu_alloc (to_add);
          to_add->value = *regu;

          // insert also in the regu_constant_list to ensure compatibility
          pt_add_regu_var_to_list (&regu_constant_list, to_add);
        }

      aggregate_list->domain = pt_xasl_node_to_domain (parser, tree);
      regu_dbval_type_init (aggregate_list->accumulator.value, pt_node_to_db_type (tree));
      if (aggregate_list->function == PT_GROUP_CONCAT)
        {
          group_concat_sep_node_save = tree->info.function.arg_list->next;
          /* store SEPARATOR for GROUP_CONCAT */
          if (group_concat_sep_node_save != NULL)
        {
          if (group_concat_sep_node_save->node_type == PT_VALUE
              && PT_IS_STRING_TYPE (group_concat_sep_node_save->type_enum))
            {
              pr_clone_value (&group_concat_sep_node_save->info.value.db_value,
                      aggregate_list->accumulator.value2);
              /* set the next argument pointer (the separator argument) to NULL in order to avoid impacting the
               * regu vars generation. */
              tree->info.function.arg_list->next = NULL;
              pt_register_orphan_db_value (parser, aggregate_list->accumulator.value2);
            }
          else
            {
              assert (false);
            }
        }
          else
        {
          PT_TYPE_ENUM arg_type;
          /* set default separator, if one is not specified , only if argument is not bit */
          arg_type = tree->type_enum;
          if (arg_type != PT_TYPE_BIT && arg_type != PT_TYPE_VARBIT)
            {
              char *buf = NULL;
              /* create a default separator with same type as result */
              /* size in bytes for ',' is always 1 even for nchar */
              buf = (char *) db_private_alloc (NULL, 1 + 1);
              if (buf == NULL)
            {
              PT_ERROR (parser, tree,
                    msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC,
                            MSGCAT_SEMANTIC_OUT_OF_MEMORY));
              return tree;
            }
              strcpy (buf, ",");
              qstr_make_typed_string (pt_type_enum_to_db (arg_type), aggregate_list->accumulator.value2,
                          DB_DEFAULT_PRECISION, buf, 1, TP_DOMAIN_CODESET (aggregate_list->domain),
                          TP_DOMAIN_COLLATION (aggregate_list->domain));
              aggregate_list->accumulator.value2->need_clear = true;
              pt_register_orphan_db_value (parser, aggregate_list->accumulator.value2);
            }
          else
            {
              db_make_null (aggregate_list->accumulator.value2);
            }
        }
        }
      else
        {
          regu_dbval_type_init (aggregate_list->accumulator.value2, pt_node_to_db_type (tree));
        }
      aggregate_list->opr_dbtype = pt_node_to_db_type (tree->info.function.arg_list);

      if (info->out_list && info->value_list && info->regu_list)
        {
          /* handle the buildlist case. append regu to the out_list, and create a new value to append to the
           * value_list Note: cume_dist() and percent_rank() also need special operations. */
          if (aggregate_list->function != PT_CUME_DIST && aggregate_list->function != PT_PERCENT_RANK)
        {
          // add dummy output name nodes, one for each argument
          for (PT_NODE * it_args = tree->info.function.arg_list; it_args != NULL; it_args = it_args->next)
            {
              pt_val = parser_new_node (parser, PT_VALUE);
              if (pt_val == NULL)
            {
              PT_INTERNAL_ERROR (parser, "allocate new node");
              return NULL;
            }

              pt_val->type_enum = PT_TYPE_INTEGER;
              pt_val->info.value.data_value.i = 0;
              parser_append_node (pt_val, info->out_names);
            }

          // for each element from arg_list we create a corresponding node in the value_list and regu_list
          if (pt_node_list_to_value_and_reguvar_list (parser, tree->info.function.arg_list,
                                  &value_list, &regu_position_list) == NULL)
            {
              PT_ERROR (parser, tree, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC,
                                  MSGCAT_SEMANTIC_OUT_OF_MEMORY));
              return NULL;
            }

          error_code = pt_make_constant_regu_list_from_val_list (parser, value_list, &aggregate_list->operands);
          if (error_code != NO_ERROR)
            {
              PT_ERROR (parser, tree, msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC,
                                  MSGCAT_SEMANTIC_OUT_OF_MEMORY));
              return NULL;
            }

          // this regu_list has the TYPE_POSITION type so we need to set the corresponding indexes for elements
          pt_set_regu_list_pos_descr_from_idx (regu_position_list, info->out_list->valptr_cnt);

          // until now we have constructed the value_list, regu_list and out_list
          // they are based on the current aggregate node information and we need to append them to the global
          // information, i.e in info
          pt_aggregate_info_update_value_and_reguvar_lists (info, value_list, regu_position_list,
                                    regu_constant_list);

          // also we need to update the scan_regu_list from info
          pt_aggregate_info_update_scan_regu_list (info, scan_regu_constant_list);
        }
          else
        {
          assert (regu_constant_list != NULL && regu_constant_list->next == NULL);

          /* for buildlist CUME_DIST/PERCENT_RANK, we have special treatment */
          if (pt_fix_buildlist_aggregate_cume_dist_percent_rank (parser, tree->info.function.order_by, info,
                                     regu) != NO_ERROR)
            {
              return NULL;
            }

          aggregate_list->operands = regu_constant_list;
        }
        }
      else
        {
          // handle the buildvalue case, simply uses regu as the operand
          aggregate_list->operands = regu_constant_list;
        }
    }
      else
    {
      /* We are set up for count(*). Make sure that Q_DISTINCT isn't set in this case.  Even though it is ignored
       * by the query processing proper, it seems to cause the setup code to build the extendible hash table it
       * needs for a "select count(distinct foo)" query, which adds a lot of unnecessary overhead. */
      aggregate_list->option = Q_ALL;

      aggregate_list->domain = &tp_Bigint_domain;
      regu_dbval_type_init (aggregate_list->accumulator.value, DB_TYPE_BIGINT);
      regu_dbval_type_init (aggregate_list->accumulator.value2, DB_TYPE_BIGINT);
      aggregate_list->opr_dbtype = DB_TYPE_INTEGER;

      regu_alloc (aggregate_list->operands);
      if (aggregate_list->operands == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          return NULL;
        }
      /* hack. we need to pack some domain even though we don't need one, so we'll pack the int. */
      aggregate_list->operands->value.domain = &tp_Integer_domain;
    }

      /* record the value for pt_to_regu_variable to use in "out arith" */
      tree->etc = (void *) aggregate_list->accumulator.value;

      info->head_list = aggregate_list;

      *continue_walk = PT_LIST_WALK;

      /* set percentile value for PT_PERCENTILE_CONT, PT_PERCENTILE_DISC */
      if (aggregate_list->function == PT_PERCENTILE_CONT || aggregate_list->function == PT_PERCENTILE_DISC)
    {
      percentile = tree->info.function.percentile;

      assert (percentile != NULL);

      regu = pt_to_regu_variable (parser, percentile, UNBOX_AS_VALUE);
      if (regu == NULL)
        {
          return NULL;
        }

      REGU_VARIABLE_LIST to_add;
      regu_alloc (to_add);
      to_add->value = *regu;

      /* build list */
      if (!PT_IS_CONST (percentile) && info->out_list != NULL && info->value_list != NULL
          && info->regu_list != NULL)
        {
          pointer = pt_point (parser, percentile);
          if (pointer == NULL)
        {
          PT_ERROR (parser, pointer,
                msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC,
                        MSGCAT_SEMANTIC_OUT_OF_MEMORY));
          return NULL;
        }

          /* append the name on the out list */
          info->out_names = parser_append_node (pointer, info->out_names);

          /* put percentile in value_list, out_list and regu_list */
          if (pt_node_list_to_value_and_reguvar_list (parser, pointer, &value_list, &regu_position_list) == NULL)
        {
          PT_ERROR (parser, percentile,
                msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC,
                        MSGCAT_SEMANTIC_OUT_OF_MEMORY));
          return NULL;
        }

          /* set aggregate_list->info.percentile.percentile_reguvar */
          regu_alloc (percentile_regu);
          if (percentile_regu == NULL)
        {
          return NULL;
        }

          percentile_regu->type = TYPE_CONSTANT;
          percentile_regu->domain = pt_xasl_node_to_domain (parser, pointer);
          percentile_regu->value.dbvalptr = value_list->valp->val;

          aggregate_list->info.percentile.percentile_reguvar = percentile_regu;

          /* fix count for list position */
          regu_position_list->value.value.pos_descr.pos_no = info->out_list->valptr_cnt;

          pt_aggregate_info_update_value_and_reguvar_lists (info, value_list, regu_position_list, to_add);
        }
      else
        {
          aggregate_list->info.percentile.percentile_reguvar = regu;
        }
    }

      /* GROUP_CONCAT : process ORDER BY and restore SEPARATOR node (just to keep original tree) */
      if (aggregate_list->function == PT_GROUP_CONCAT || aggregate_list->function == PT_CUME_DIST
      || aggregate_list->function == PT_PERCENT_RANK || (QPROC_IS_INTERPOLATION_FUNC (aggregate_list)
                                 && !PT_IS_CONST (tree->info.function.arg_list)))
    {
      /* Separator of GROUP_CONCAT is not a 'real' argument of GROUP_CONCAT, but for convenience it is kept in
       * 'arg_list' of PT_FUNCTION. It is not involved in sorting process, so conversion of ORDER BY to SORT_LIST
       * must be performed before restoring separator argument into the arg_list */
      tree = pt_fix_interpolation_aggregate_function_order_by (parser, tree);
      if (tree == NULL)
        {
          /* error must be set */
          assert (pt_has_error (parser));

          return NULL;
        }

      if (tree->info.function.order_by != NULL)
        {
          /* convert to SORT_LIST */
          aggregate_list->sort_list =
        pt_agg_orderby_to_sort_list (parser, tree->info.function.order_by, tree->info.function.arg_list);
        }
      else
        {
          aggregate_list->sort_list = NULL;
        }

      /* restore group concat separator node */
      tree->info.function.arg_list->next = group_concat_sep_node_save;
    }
      else
    {
      /* GROUP_CONCAT, MEDIAN, PERCENTILE_CONT and PERCENTILE_DISC aggs support ORDER BY. We ignore ORDER BY for
       * MEDIAN/PERCENTILE_CONT/PERCENTILE_DISC, when arg_list is a constant. */
      assert (QPROC_IS_INTERPOLATION_FUNC (aggregate_list) || tree->info.function.order_by == NULL);

      assert (group_concat_sep_node_save == NULL);
    }
    }

  if (tree->node_type == PT_DOT_)
    {
      /* This path must have already appeared in the group-by, and is resolved. Convert it to a name so that we can use
       * it to get the correct list position later. */
      PT_NODE *next = tree->next;
      tree = tree->info.dot.arg2;
      tree->next = next;
    }

  if (tree->node_type == PT_SELECT || tree->node_type == PT_UNION || tree->node_type == PT_INTERSECTION
      || tree->node_type == PT_DIFFERENCE)
    {
      /* this is a sub-query. It has its own aggregation scope. Do not proceed down the leaves. */
      *continue_walk = PT_LIST_WALK;
    }
  else if (tree->node_type == PT_EXPR
       && (tree->info.expr.op == PT_CURRENT_VALUE || tree->info.expr.op == PT_NEXT_VALUE))
    {
      /* Do not proceed down the leaves. */
      *continue_walk = PT_LIST_WALK;
    }
  else if (PT_IS_METHOD (tree))
    {
      /* Do not proceed down the leaves */
      *continue_walk = PT_LIST_WALK;
    }

  if (tree->node_type == PT_NAME)
    {
      if (!pt_find_name (parser, tree, info->out_names) && (info->out_list && info->value_list && info->regu_list))
    {
      pointer = pt_point (parser, tree);
      if (pointer == NULL)
        {
          PT_ERROR (parser, pointer,
            msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC,
                    MSGCAT_SEMANTIC_OUT_OF_MEMORY));
          return NULL;
        }

      /* append the name on the out list */
      info->out_names = parser_append_node (pointer, info->out_names);

      if (pt_node_list_to_value_and_reguvar_list (parser, pointer, &value_list, &regu_position_list) == NULL)
        {
          PT_ERROR (parser, tree,
            msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC,
                    MSGCAT_SEMANTIC_OUT_OF_MEMORY));
          return NULL;
        }

      /* fix count for list position */
      regu_position_list->value.value.pos_descr.pos_no = info->out_list->valptr_cnt;

      regu = pt_to_regu_variable (parser, tree, UNBOX_AS_VALUE);
      if (regu == NULL)
        {
          return NULL;
        }

      REGU_VARIABLE_LIST to_add;
      regu_alloc (to_add);
      to_add->value = *regu;

      // insert also in the regu_constant_list to ensure compatibility
      pt_add_regu_var_to_list (&regu_constant_list, to_add);

      pt_aggregate_info_update_value_and_reguvar_lists (info, value_list, regu_position_list, regu_constant_list);
    }
      *continue_walk = PT_LIST_WALK;
    }

  if (tree->node_type == PT_SPEC || tree->node_type == PT_DATA_TYPE)
    {
      /* These node types cannot have sub-expressions. Do not proceed down the leaves */
      *continue_walk = PT_LIST_WALK;
    }
  return tree;
}

/*
 * pt_find_attribute () -
 *   return: index of a name in an attribute symbol list,
 *           or -1 if the name is not found in the list
 *   parser(in):
 *   name(in):
 *   attributes(in):
 */
int
pt_find_attribute (PARSER_CONTEXT * parser, const PT_NODE * name, const PT_NODE * attributes)
{
  PT_NODE *attr, *save_attr;
  int i = 0;

  if (name)
    {
      CAST_POINTER_TO_NODE (name);

      if (name->node_type == PT_NAME)
    {
      for (attr = (PT_NODE *) attributes; attr != NULL; attr = attr->next)
        {
          save_attr = attr; /* save */

          CAST_POINTER_TO_NODE (attr);

          /* are we looking up sort_spec list ? currently only group by causes this case. */
          if (attr->node_type == PT_SORT_SPEC)
        {
          attr = attr->info.sort_spec.expr;
        }

          if (!name->info.name.resolved)
        {
          /* are we looking up a path expression name? currently only group by causes this case. */
          if (attr->node_type == PT_DOT_ && pt_name_equal (parser, (PT_NODE *) name, attr->info.dot.arg2))
            {
              return i;
            }
        }

          if (pt_name_equal (parser, (PT_NODE *) name, attr))
        {
          return i;
        }
          i++;

          attr = save_attr; /* restore */
        }
    }
    }

  return -1;
}

static void
pt_optimize_min_max_list (PARSER_CONTEXT * parser, PT_NODE * node, QO_PLAN * plan, AGGREGATE_TYPE * aggregate)
{
  bool dummy;
  assert (node->node_type == PT_FUNCTION);
  PT_NODE *arg_list = node->info.function.arg_list;
  PT_NODE *iscan_sort_list = qo_plan_compute_iscan_sort_list (plan, NULL, &dummy, true);

  switch (aggregate->function)
    {
    case PT_MIN:
      if (pt_sort_spec_cover_for_min_max (parser, QO_ENV_PT_TREE ((plan->info)->env), iscan_sort_list, arg_list))
    {
      aggregate->flag.min_max_optimized = true;
      aggregate->flag.part_key_descending = (iscan_sort_list->info.sort_spec.asc_or_desc == PT_DESC);
    }
      else
    {
      aggregate->flag.min_max_optimized = false;
    }
      break;
    case PT_MAX:
      if (pt_sort_spec_cover_for_min_max (parser, QO_ENV_PT_TREE ((plan->info)->env), iscan_sort_list, arg_list))
    {
      aggregate->flag.min_max_optimized = true;
      aggregate->flag.part_key_descending = (iscan_sort_list->info.sort_spec.asc_or_desc == PT_DESC);
    }
      else
    {
      aggregate->flag.min_max_optimized = false;
    }
      break;
    default:
      break;
    }
}

static void
pt_set_access_spec_for_aggregation (PARSER_CONTEXT * parser, AGGREGATE_TYPE * aggregate, ACCESS_SPEC_TYPE * access_spec)
{
  AGGREGATE_TYPE *agg;
  bool min_max_scan = false;
  bool min_max_only_scan = true;

  for (agg = aggregate; agg != NULL; agg = agg->next)
    {
      switch (agg->function)
    {
    case PT_MIN:
      if (agg->flag.min_max_optimized)
        {
          min_max_scan = true;
        }
      else
        {
          min_max_only_scan = false;
        }
      break;
    case PT_MAX:
      if (agg->flag.min_max_optimized)
        {
          min_max_scan = true;
        }
      else
        {
          min_max_only_scan = false;
        }
      break;
    default:
      min_max_only_scan = false;
      break;
    }
    }
  if (min_max_only_scan && min_max_scan)
    {
      ACCESS_SPEC_SET_FLAG (access_spec, ACCESS_SPEC_FLAG_ONLY_MIN_MAX_SCAN);
    }
}

/*
 * pt_index_value () -
 *   return: the DB_VALUE at the index position in a VAL_LIST
 *   value(in):
 *   index(in):
 */
static DB_VALUE *
pt_index_value (const VAL_LIST * value, int index)
{
  QPROC_DB_VALUE_LIST dbval_list;
  DB_VALUE *dbval = NULL;

  if (value && index >= 0)
    {
      dbval_list = value->valp;
      while (dbval_list && index)
    {
      dbval_list = dbval_list->next;
      index--;
    }

      if (dbval_list)
    {
      dbval = dbval_list->val;
    }
    }

  return dbval;
}

/*
 * pt_to_aggregate () - Generates an aggregate list from a select node
 *   return: aggregate XASL node
 *   parser(in): parser context
 *   select_node(in): SELECT statement node
 *   out_list(in): outptr list to generate intermediate file
 *   value_list(in): value list
 *   regu_list(in): regulist to read values from intermediate file
 *   scan_regu_list(in): regulist to read values during initial scan
 *   out_names(in): outptr name nodes
 *   grbynum_valp(in): groupby_num() dbvalue
 */
static AGGREGATE_TYPE *
pt_to_aggregate (PARSER_CONTEXT * parser, PT_NODE * select_node, OUTPTR_LIST * out_list, VAL_LIST * value_list,
         REGU_VARIABLE_LIST regu_list, REGU_VARIABLE_LIST scan_regu_list, PT_NODE * out_names,
         DB_VALUE ** grbynum_valp, QO_PLAN * plan)
{
  PT_NODE *select_list, *from, *where, *having;
  AGGREGATE_INFO info;

  select_list = select_node->info.query.q.select.list;
  from = select_node->info.query.q.select.from;
  where = select_node->info.query.q.select.where;
  having = select_node->info.query.q.select.having;

  info.head_list = NULL;
  info.out_list = out_list;
  info.value_list = value_list;
  info.regu_list = regu_list;
  info.scan_regu_list = scan_regu_list;
  info.out_names = out_names;
  info.grbynum_valp = grbynum_valp;
  info.qo_plan = plan;

  /* init */
  info.class_name = NULL;
  info.flag_agg_optimize = false;
  info.flag_agg_min_max_optimized = false;

  /* TODO : for multi table */
  if (!select_node->info.query.q.select.group_by && !select_node->info.query.order_by
      && !select_node->info.query.orderby_for && from->next == NULL)
    {
      info.flag_agg_min_max_optimized = true;
    }

  if (pt_is_single_tuple (parser, select_node))
    {
      if (where == NULL && pt_length_of_list (from) == 1 && pt_length_of_list (from->info.spec.flat_entity_list) == 1
      && from->info.spec.only_all != PT_ALL)
    {
      if (from->info.spec.entity_name)
        {
          info.class_name = from->info.spec.entity_name->info.name.original;
          info.flag_agg_optimize = true;
        }
    }
    }

  select_node->info.query.q.select.list =
    parser_walk_tree (parser, select_list, pt_to_aggregate_node, &info, pt_continue_walk, NULL);

  select_node->info.query.q.select.having =
    parser_walk_tree (parser, having, pt_to_aggregate_node, &info, pt_continue_walk, NULL);

  return info.head_list;
}


/*
 * pt_make_table_info () - Sets up symbol table entry for an entity spec
 *   return:
 *   parser(in):
 *   table_spec(in):
 */
static TABLE_INFO *
pt_make_table_info (PARSER_CONTEXT * parser, PT_NODE * table_spec)
{
  TABLE_INFO *table_info;

  table_info = pt_table_info_alloc ();
  if (table_info == NULL)
    {
      return NULL;
    }

  table_info->class_spec = table_spec;
  if (table_spec->info.spec.range_var)
    {
      table_info->exposed = table_spec->info.spec.range_var->info.name.original;
    }

  table_info->spec_id = table_spec->info.spec.id;

  /* for classes, it is safe to prune unreferenced attributes. we do not have the same luxury with derived tables, so
   * get them all (and in order). */
  table_info->attribute_list = (table_spec->info.spec.flat_entity_list != NULL && PT_SPEC_IS_ENTITY (table_spec))
    ? table_spec->info.spec.referenced_attrs : table_spec->info.spec.as_attr_list;

  table_info->value_list = pt_make_val_list (parser, table_info->attribute_list);

  if (!table_info->value_list)
    {
      PT_ERRORm (parser, table_info->attribute_list, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      return NULL;
    }

  return table_info;
}


/*
 * pt_push_fetch_spec_info () - Sets up symbol table information
 *                              for a select statement
 *   return:
 *   parser(in):
 *   symbols(in):
 *   fetch_spec(in):
 */
static SYMBOL_INFO *
pt_push_fetch_spec_info (PARSER_CONTEXT * parser, SYMBOL_INFO * symbols, PT_NODE * fetch_spec)
{
  PT_NODE *spec;
  TABLE_INFO *table_info;

  for (spec = fetch_spec; spec != NULL; spec = spec->next)
    {
      table_info = pt_make_table_info (parser, spec);
      if (table_info == NULL)
    {
      symbols = NULL;
      break;
    }
      else if (symbols != NULL)
    {
      table_info->next = symbols->table_info;
      table_info->is_fetch = 1;
    }

      if (symbols != NULL)
    {
      symbols->table_info = table_info;
    }

      symbols = pt_push_fetch_spec_info (parser, symbols, spec->info.spec.path_entities);
    }

  return symbols;
}

/*
 * pt_push_symbol_info () - Sets up symbol table information
 *                          for a select statement
 *   return:
 *   parser(in):
 *   select_node(in):
 */
static SYMBOL_INFO *
pt_push_symbol_info (PARSER_CONTEXT * parser, PT_NODE * select_node)
{
  PT_NODE *table_spec;
  SYMBOL_INFO *symbols = NULL;
  TABLE_INFO *table_info;
  PT_NODE *from_list;

  symbols = pt_symbol_info_alloc ();

  if (symbols)
    {
      /* push symbols on stack */
      symbols->stack = parser->symbols;
      parser->symbols = symbols;

      symbols->query_node = select_node;

      if (select_node->node_type == PT_SELECT)
    {
      /* remove pseudo specs */
      select_node->info.query.q.select.from =
        pt_filter_pseudo_specs (parser, select_node->info.query.q.select.from);

      from_list = select_node->info.query.q.select.from;

      for (table_spec = from_list; table_spec != NULL; table_spec = table_spec->next)
        {
          table_info = pt_make_table_info (parser, table_spec);
          if (!table_info)
        {
          symbols = NULL;
          break;
        }
          table_info->next = symbols->table_info;
          symbols->table_info = table_info;

          symbols = pt_push_fetch_spec_info (parser, symbols, table_spec->info.spec.path_entities);
          if (!symbols)
        {
          break;
        }
        }

      if (symbols)
        {
          symbols->current_class = NULL;
          symbols->current_listfile = NULL;
          symbols->listfile_unbox = UNBOX_AS_VALUE;
        }
    }
    }

  return symbols;
}


/*
 * pt_pop_symbol_info () - Cleans up symbol table information
 *                         for a select statement
 *   return: none
 *   parser(in):
 *   select_node(in):
 */
static void
pt_pop_symbol_info (PARSER_CONTEXT * parser)
{
  SYMBOL_INFO *symbols = NULL;

  if (parser->symbols)
    {
      /* allocated from pt_alloc_packing_buf */
      symbols = parser->symbols->stack;
      parser->symbols = symbols;
    }
  else
    {
      if (!pt_has_error (parser))
    {
      PT_INTERNAL_ERROR (parser, "generate");
    }
    }
}


/*
 * pt_make_access_spec () - Create an initialized ACCESS_SPEC_TYPE structure,
 *                      ready to be specialized for class or list
 *   return:
 *   spec_type(in):
 *   access(in):
 *   indexptr(in):
 *   where_key(in):
 *   where_pred(in):
 *   where_range(in):
 */
static ACCESS_SPEC_TYPE *
pt_make_access_spec (TARGET_TYPE spec_type, ACCESS_METHOD access, INDX_INFO * indexptr, PRED_EXPR * where_key,
             PRED_EXPR * where_pred, PRED_EXPR * where_range)
{
  ACCESS_SPEC_TYPE *spec = NULL;

  /* validation check */
  if (access == ACCESS_METHOD_INDEX)
    {
      assert (indexptr != NULL);
      if (indexptr)
    {
      if (indexptr->coverage)
        {
          assert (where_pred == NULL);  /* no data-filter */
          if (where_pred == NULL)
        {
          spec = regu_spec_alloc (spec_type);
        }
        }
      else
        {
          spec = regu_spec_alloc (spec_type);
        }
    }
    }
  else
    {
      spec = regu_spec_alloc (spec_type);
    }

  if (spec)
    {
      spec->type = spec_type;
      spec->access = access;
      spec->indexptr = indexptr;
      spec->where_key = where_key;
      spec->where_pred = where_pred;
      spec->where_range = where_range;
      spec->next = NULL;
      spec->pruning_type = DB_NOT_PARTITIONED_CLASS;
    }

  return spec;
}


/*
 * pt_cnt_attrs () - Count the number of regu variables in the list that
 *                   are coming from the heap (ATTR_ID)
 *   return:
 *   attr_list(in):
 */
static int
pt_cnt_attrs (const REGU_VARIABLE_LIST attr_list)
{
  int cnt = 0;
  REGU_VARIABLE_LIST tmp;

  for (tmp = attr_list; tmp; tmp = tmp->next)
    {
      if ((tmp->value.type == TYPE_ATTR_ID) || (tmp->value.type == TYPE_SHARED_ATTR_ID)
      || (tmp->value.type == TYPE_CLASS_ATTR_ID))
    {
      cnt++;
    }
      else if (tmp->value.type == TYPE_FUNC)
    {
      /* need to check all the operands for the function */
      cnt += pt_cnt_attrs (tmp->value.value.funcp->operand);
    }
      else if (tmp->value.type == TYPE_SP)
    {
      cnt += pt_cnt_attrs (tmp->value.value.sp_ptr->args);
    }
    }

  return cnt;
}


/*
 * pt_fill_in_attrid_array () - Fill in the attrids of the regu variables
 *                              in the list that are comming from the heap
 *   return:
 *   attr_list(in):
 *   attr_array(in):
 *   next_pos(in): holds the next spot in the array to be filled in with the
 *                 next attrid
 */
static void
pt_fill_in_attrid_array (REGU_VARIABLE_LIST attr_list, ATTR_ID * attr_array, int *next_pos)
{
  REGU_VARIABLE_LIST tmp;

  for (tmp = attr_list; tmp; tmp = tmp->next)
    {
      if ((tmp->value.type == TYPE_ATTR_ID) || (tmp->value.type == TYPE_SHARED_ATTR_ID)
      || (tmp->value.type == TYPE_CLASS_ATTR_ID))
    {
      attr_array[*next_pos] = tmp->value.value.attr_descr.id;
      *next_pos = *next_pos + 1;
    }
      else if (tmp->value.type == TYPE_FUNC)
    {
      /* need to check all the operands for the function */
      pt_fill_in_attrid_array (tmp->value.value.funcp->operand, attr_array, next_pos);
    }
      else if (tmp->value.type == TYPE_SP)
    {
      pt_fill_in_attrid_array (tmp->value.value.sp_ptr->args, attr_array, next_pos);
    }
    }
}

/*
 * pt_make_class_access_spec () - Create an initialized
 *                                ACCESS_SPEC_TYPE TARGET_CLASS structure
 *   return:
 *   parser(in):
 *   flat(in):
 *   class(in):
 *   scan_type(in):
 *   access(in):
 *   indexptr(in):
 *   where_key(in):
 *   where_pred(in):
 *   where_range(in):
 *   attr_list_key(in):
 *   attr_list_pred(in):
 *   attr_list_rest(in):
 *   attr_list_range(in):
 *   cache_key(in):
 *   cache_pred(in):
 *   cache_rest(in):
 *   cache_range(in):
 *   schema_type(in):
 */
static ACCESS_SPEC_TYPE *
pt_make_class_access_spec (PARSER_CONTEXT * parser, PT_NODE * flat, DB_OBJECT * class_, TARGET_TYPE scan_type,
               ACCESS_METHOD access, INDX_INFO * indexptr, PRED_EXPR * where_key, PRED_EXPR * where_pred,
               PRED_EXPR * where_range, REGU_VARIABLE_LIST attr_list_key, REGU_VARIABLE_LIST attr_list_pred,
               REGU_VARIABLE_LIST attr_list_rest, REGU_VARIABLE_LIST attr_list_range,
               OUTPTR_LIST * output_val_list, REGU_VARIABLE_LIST regu_val_list,
               HEAP_CACHE_ATTRINFO * cache_key, HEAP_CACHE_ATTRINFO * cache_pred,
               HEAP_CACHE_ATTRINFO * cache_rest, HEAP_CACHE_ATTRINFO * cache_range,
               ACCESS_SCHEMA_TYPE schema_type, DB_VALUE ** cache_recordinfo,
               REGU_VARIABLE_LIST reserved_val_list)
{
  ACCESS_SPEC_TYPE *spec;
  HFID *hfid;
  OID *cls_oid;
  int attrnum;

  spec = pt_make_access_spec (scan_type, access, indexptr, where_key, where_pred, where_range);
  if (spec == NULL)
    {
      return NULL;
    }

  assert (class_ != NULL);

  if (locator_fetch_class (class_, DB_FETCH_READ) == NULL)
    {
      PT_ERRORc (parser, flat, er_msg ());
      return NULL;
    }

  hfid = sm_get_ch_heap (class_);
  if (hfid == NULL)
    {
      return NULL;
    }

  cls_oid = WS_OID (class_);
  if (cls_oid == NULL || OID_ISNULL (cls_oid))
    {
      return NULL;
    }

  if (sm_partitioned_class_type (class_, &spec->pruning_type, NULL, NULL) != NO_ERROR)
    {
      PT_ERRORc (parser, flat, er_msg ());
      return NULL;
    }

  spec->s.cls_node.cls_regu_list_key = attr_list_key;
  spec->s.cls_node.cls_regu_list_pred = attr_list_pred;
  spec->s.cls_node.cls_regu_list_rest = attr_list_rest;
  spec->s.cls_node.cls_regu_list_range = attr_list_range;
  spec->s.cls_node.cls_output_val_list = output_val_list;
  spec->s.cls_node.cls_regu_val_list = regu_val_list;
  spec->s.cls_node.hfid = *hfid;
  spec->s.cls_node.cls_oid = *cls_oid;

  spec->s.cls_node.num_attrs_key = pt_cnt_attrs (attr_list_key);
  spec->s.cls_node.attrids_key = regu_int_array_alloc (spec->s.cls_node.num_attrs_key);

  assert_release (spec->s.cls_node.num_attrs_key != 0
          || (spec->s.cls_node.num_attrs_key == 0 && attr_list_key == NULL));

  attrnum = 0;
  /* for multi-column index, need to modify attr_id */
  pt_fill_in_attrid_array (attr_list_key, spec->s.cls_node.attrids_key, &attrnum);
  spec->s.cls_node.cache_key = cache_key;
  spec->s.cls_node.num_attrs_pred = pt_cnt_attrs (attr_list_pred);
  spec->s.cls_node.attrids_pred = regu_int_array_alloc (spec->s.cls_node.num_attrs_pred);
  attrnum = 0;
  pt_fill_in_attrid_array (attr_list_pred, spec->s.cls_node.attrids_pred, &attrnum);
  spec->s.cls_node.cache_pred = cache_pred;
  spec->s.cls_node.num_attrs_rest = pt_cnt_attrs (attr_list_rest);
  spec->s.cls_node.attrids_rest = regu_int_array_alloc (spec->s.cls_node.num_attrs_rest);
  attrnum = 0;
  pt_fill_in_attrid_array (attr_list_rest, spec->s.cls_node.attrids_rest, &attrnum);
  spec->s.cls_node.cache_rest = cache_rest;
  spec->s.cls_node.num_attrs_range = pt_cnt_attrs (attr_list_range);
  spec->s.cls_node.attrids_range = regu_int_array_alloc (spec->s.cls_node.num_attrs_range);
  attrnum = 0;
  pt_fill_in_attrid_array (attr_list_range, spec->s.cls_node.attrids_range, &attrnum);
  spec->s.cls_node.cache_range = cache_range;
  spec->s.cls_node.schema_type = schema_type;
  spec->s.cls_node.cache_reserved = cache_recordinfo;
  if (access == ACCESS_METHOD_SEQUENTIAL_RECORD_INFO)
    {
      spec->s.cls_node.num_attrs_reserved = HEAP_RECORD_INFO_COUNT;
    }
  else if (access == ACCESS_METHOD_SEQUENTIAL_PAGE_SCAN)
    {
      spec->s.cls_node.num_attrs_reserved = HEAP_PAGE_INFO_COUNT;
    }
  else if (access == ACCESS_METHOD_INDEX_KEY_INFO)
    {
      spec->s.cls_node.num_attrs_reserved = BTREE_KEY_INFO_COUNT;
    }
  else if (access == ACCESS_METHOD_INDEX_NODE_INFO)
    {
      spec->s.cls_node.num_attrs_reserved = BTREE_NODE_INFO_COUNT;
    }
  else
    {
      spec->s.cls_node.num_attrs_reserved = 0;
    }
  spec->s.cls_node.cls_regu_list_reserved = reserved_val_list;

  return spec;
}

static void
pt_create_json_table_column (PARSER_CONTEXT * parser, PT_NODE * jt_column, TABLE_INFO * tbl_info,
                 json_table_column & col_result)
{
  col_result.m_function = jt_column->info.json_table_column_info.func;
  col_result.m_output_value_pointer = pt_index_value (tbl_info->value_list,
                              pt_find_attribute (parser,
                                     jt_column->info.json_table_column_info.name,
                                     tbl_info->attribute_list));
  if (col_result.m_output_value_pointer == NULL)
    {
      assert (false);
    }

  col_result.m_domain = pt_xasl_node_to_domain (parser, jt_column);

  if (jt_column->info.json_table_column_info.path != NULL)
    {
      col_result.m_path = jt_column->info.json_table_column_info.path;
    }

  col_result.m_column_name = (char *) jt_column->info.json_table_column_info.name->info.name.original;

  col_result.m_on_empty = jt_column->info.json_table_column_info.on_empty;
  col_result.m_on_error = jt_column->info.json_table_column_info.on_error;
}

//
// pt_make_json_table_spec_node_internal () - recursive function to generate json table access tree
//
// parser (in)         : parser context
// jt_node_info (in)   : json table parser node info
// current_id (in/out) : as input ID for this node, output next ID (after all nested nodes in current branch)
// tbl_info (in)       : table info cache
// result (out)        : a node in json table access tree based on json table node info
//
static void
pt_make_json_table_spec_node_internal (PARSER_CONTEXT * parser, PT_JSON_TABLE_NODE_INFO * jt_node_info,
                       size_t & current_id, TABLE_INFO * tbl_info, json_table_node & result)
{
  size_t i = 0;
  PT_NODE *itr;

  // copy path
  result.m_path = (char *) jt_node_info->path;

  // after set the id, increment
  result.m_id = current_id++;

  // nodes that have wildcard in their paths are the only ones that are iterable
  result.m_is_iterable_node = false;
  if (result.m_path)
    {
      result.m_is_iterable_node = db_json_path_contains_wildcard (result.m_path);
    }

  // create columns
  result.m_output_columns_size = 0;
  for (itr = jt_node_info->columns; itr != NULL; itr = itr->next, ++result.m_output_columns_size)
    ;

  result.m_output_columns =
    (json_table_column *) pt_alloc_packing_buf ((int) (sizeof (json_table_column) * result.m_output_columns_size));

  for (itr = jt_node_info->columns, i = 0; itr != NULL; itr = itr->next, i++)
    {
      pt_create_json_table_column (parser, itr, tbl_info, result.m_output_columns[i]);
    }

  // create children
  result.m_nested_nodes_size = 0;
  for (itr = jt_node_info->nested_paths; itr != NULL; itr = itr->next, ++result.m_nested_nodes_size)
    ;

  result.m_nested_nodes =
    (json_table_node *) pt_alloc_packing_buf ((int) (sizeof (json_table_node) * result.m_nested_nodes_size));

  for (itr = jt_node_info->nested_paths, i = 0; itr != NULL; itr = itr->next, i++)
    {
      pt_make_json_table_spec_node_internal (parser, &itr->info.json_table_node_info, current_id, tbl_info,
                         result.m_nested_nodes[i]);
    }
}

//
// pt_make_json_table_spec_node () - create json table access tree
//
// return            : pointer to generated json_table_node
// parser (in)       : parser context
// json_table (in)   : json table parser node info
// start_id (in/out) : output total node count (root + nested)
// tbl_info (in)     : table info cache
//
static json_table_node *
pt_make_json_table_spec_node (PARSER_CONTEXT * parser, PT_JSON_TABLE_INFO * json_table, size_t & start_id,
                  TABLE_INFO * tbl_info)
{
  json_table_node *root_node = (json_table_node *) pt_alloc_packing_buf (sizeof (json_table_node));
  pt_make_json_table_spec_node_internal (parser, &json_table->tree->info.json_table_node_info, start_id, tbl_info,
                     *root_node);
  return root_node;
}

//
// pt_make_json_table_access_spec () - make json access spec
//
// return            : pointer to access spec
// parser (in)       : parser context
// json_reguvar (in) : reguvar for json table expression
// where_pred (in)   : json table scan filter predicate
// json_table (in)   : json table parser node info
// tbl_info (in)     : table info cache
//
static ACCESS_SPEC_TYPE *
pt_make_json_table_access_spec (PARSER_CONTEXT * parser, REGU_VARIABLE * json_reguvar, PRED_EXPR * where_pred,
                PT_JSON_TABLE_INFO * json_table, TABLE_INFO * tbl_info)
{
  ACCESS_SPEC_TYPE *spec;
  size_t start_id = 0;

  spec = pt_make_access_spec (TARGET_JSON_TABLE, ACCESS_METHOD_JSON_TABLE, NULL, NULL, where_pred, NULL);

  if (spec)
    {
      spec->s.json_table_node.m_root_node = pt_make_json_table_spec_node (parser, json_table, start_id, tbl_info);
      spec->s.json_table_node.m_json_reguvar = json_reguvar;
      // each node will have its own incremental id, so we can count the nr of nodes based on this identifier
      spec->s.json_table_node.m_node_count = start_id;
    }

  return spec;
}

/*
 * pt_make_list_access_spec () - Create an initialized
 *                               ACCESS_SPEC_TYPE TARGET_LIST structure
 *   return:
 *   xasl(in):
 *   access(in):
 *   indexptr(in):
 *   where_pred(in):
 *   attr_list_pred(in):
 *   attr_list_rest(in):
 */
static ACCESS_SPEC_TYPE *
pt_make_list_access_spec (XASL_NODE * xasl, ACCESS_METHOD access, INDX_INFO * indexptr, PRED_EXPR * where_pred,
              REGU_VARIABLE_LIST attr_list_pred, REGU_VARIABLE_LIST attr_list_rest,
              REGU_VARIABLE_LIST attr_list_build, REGU_VARIABLE_LIST attr_list_probe)
{
  ACCESS_SPEC_TYPE *spec;

  if (!xasl)
    {
      return NULL;
    }

  spec = pt_make_access_spec (TARGET_LIST, access, indexptr, NULL, where_pred, NULL);

  if (spec)
    {
      spec->s.list_node.list_regu_list_pred = attr_list_pred;
      spec->s.list_node.list_regu_list_rest = attr_list_rest;
      spec->s.list_node.list_regu_list_build = attr_list_build;
      spec->s.list_node.list_regu_list_probe = attr_list_probe;
      spec->s.list_node.xasl_node = xasl;
    }

  return spec;
}

/*
 * pt_make_showstmt_access_spec () - Create an initialized
 *                               ACCESS_SPEC_TYPE TARGET_SHOWSTMT structure
 *   return:
 *   where_pred(in):
 *   show_type(in):
 *   arg_list(in):
 */
static ACCESS_SPEC_TYPE *
pt_make_showstmt_access_spec (PRED_EXPR * where_pred, SHOWSTMT_TYPE show_type, REGU_VARIABLE_LIST arg_list)
{
  ACCESS_SPEC_TYPE *spec;

  spec = pt_make_access_spec (TARGET_SHOWSTMT, ACCESS_METHOD_SEQUENTIAL, NULL, NULL, where_pred, NULL);

  if (spec)
    {
      spec->s.showstmt_node.show_type = show_type;
      spec->s.showstmt_node.arg_list = arg_list;
    }

  return spec;
}


/*
 * pt_make_set_access_spec () - Create an initialized
 *                              ACCESS_SPEC_TYPE TARGET_SET structure
 *   return:
 *   set_expr(in):
 *   access(in):
 *   indexptr(in):
 *   where_pred(in):
 *   attr_list(in):
 */
static ACCESS_SPEC_TYPE *
pt_make_set_access_spec (REGU_VARIABLE * set_expr, ACCESS_METHOD access, INDX_INFO * indexptr, PRED_EXPR * where_pred,
             REGU_VARIABLE_LIST attr_list)
{
  ACCESS_SPEC_TYPE *spec;

  if (!set_expr)
    {
      return NULL;
    }

  spec = pt_make_access_spec (TARGET_SET, access, indexptr, NULL, where_pred, NULL);

  if (spec)
    {
      spec->s.set_node.set_regu_list = attr_list;
      spec->s.set_node.set_ptr = set_expr;
    }

  return spec;
}


/*
 * pt_make_cselect_access_spec () - Create an initialized
 *                  ACCESS_SPEC_TYPE TARGET_METHOD structure
 *   return:
 *   xasl(in):
 *   sig_array(in):
 *   access(in):
 *   indexptr(in):
 *   where_pred(in):
 *   attr_list(in):
 */
static ACCESS_SPEC_TYPE *
pt_make_cselect_access_spec (XASL_NODE * xasl, PL_SIGNATURE_ARRAY_TYPE * sig_array, ACCESS_METHOD access,
                 INDX_INFO * indexptr, PRED_EXPR * where_pred, REGU_VARIABLE_LIST attr_list)
{
  ACCESS_SPEC_TYPE *spec;

  if (!xasl)
    {
      return NULL;
    }

  spec = pt_make_access_spec (TARGET_METHOD, access, indexptr, NULL, where_pred, NULL);

  if (spec)
    {
      spec->s.method_node.method_regu_list = attr_list;
      spec->s.method_node.xasl_node = xasl;
      spec->s.method_node.sig_array = sig_array;
    }

  return spec;
}

/*
 * pt_make_dblink_access_spec () - Create an initialized
 *                  ACCESS_SPEC_TYPE TARGET_DBLINK structure
 *   return:
 *   xasl(in):
 *   access(in):
 *   attr_list(in):
 */
ACCESS_SPEC_TYPE *
pt_make_dblink_access_spec (ACCESS_METHOD access,
                PRED_EXPR * where_pred,
                REGU_VARIABLE_LIST pred_list,
                REGU_VARIABLE_LIST attr_list, char *url, char *user, char *password,
                int host_var_count, int *host_var_index, char *sql)
{
  ACCESS_SPEC_TYPE *spec;

  spec = pt_make_access_spec (TARGET_DBLINK, access, NULL, NULL, where_pred, NULL);
  if (spec)
    {
      spec->s.dblink_node.dblink_regu_list_rest = attr_list;
      spec->s.dblink_node.dblink_regu_list_pred = pred_list;
      spec->s.dblink_node.conn_url = url;
      spec->s.dblink_node.conn_user = user;
      spec->s.dblink_node.conn_password = password;
      spec->s.dblink_node.conn_sql = sql;
      spec->s.dblink_node.host_var_count = host_var_count;
      spec->s.dblink_node.host_var_index = host_var_index;
    }

  return spec;
}

/*
 * pt_to_pos_descr () - Translate PT_SORT_SPEC node to QFILE_TUPLE_VALUE_POSITION node
 *   return:
 *   parser(in):
 *   pos_p(out):
 *   node(in):
 *   root(in):
 *   referred_node(in/out): optional parameter to get real name or expression node
 *                          referred by a position
 */
void
pt_to_pos_descr (PARSER_CONTEXT * parser, QFILE_TUPLE_VALUE_POSITION * pos_p, PT_NODE * node, PT_NODE * root,
         PT_NODE ** referred_node, bool for_min_max_optimize)
{
  PT_NODE *temp;
  char *node_str = NULL;
  int i;

  pos_p->pos_no = -1;       /* init */
  pos_p->dom = NULL;        /* init */

  if (referred_node != NULL)
    {
      *referred_node = NULL;
    }

  switch (root->node_type)
    {
    case PT_SELECT:

      if (node->node_type == PT_EXPR || node->node_type == PT_DOT_)
    {
      unsigned int save_custom;

      save_custom = parser->custom_print;   /* save */
      parser->custom_print |= PT_CONVERT_RANGE;

      node_str = parser_print_tree (parser, node);

      parser->custom_print = save_custom;   /* restore */
    }

      /* when do lex analysis, will CHECK nodes in groupby or orderby list whether in select_item list by alias and
       * positions,if yes,some substitution will done,so can not just compare by node_type, alias_print also be
       * considered. As function resolve_alias_in_name_node(), two round comparison will be done : first compare with
       * node_type, if not found, second round check will execute if alias_print is not NULL, compare it with
       * select_item whose alias_print is also not NULL. */

      /* first round search */
      i = 1;            /* PT_SORT_SPEC pos_no start from 1 */
      for (temp = root->info.query.q.select.list; temp != NULL; temp = temp->next)
    {
      if (node->node_type == temp->node_type)
        {
          if (node->node_type == PT_NAME)
        {
          if (pt_name_equal (parser, node, temp))
            {
              pos_p->pos_no = i;
            }
        }
          else if (node->node_type == PT_EXPR || node->node_type == PT_DOT_)
        {
          if (pt_str_compare (node_str, parser_print_tree (parser, temp), CASE_INSENSITIVE) == 0)
            {
              pos_p->pos_no = i;
            }
        }
        }
      else if (pt_check_compatible_node_for_orderby (parser, temp, node))
        {
          pos_p->pos_no = i;
        }
      else if (for_min_max_optimize && pt_check_compatible_node_for_min_max_optimize (parser, temp, node))
        {
          pos_p->pos_no = i;
        }

      if (pos_p->pos_no == -1)
        {           /* not found match */
          if (node->node_type == PT_VALUE && node->alias_print == NULL)
        {
          assert_release (node->node_type == PT_VALUE && node->type_enum == PT_TYPE_INTEGER);
          if (node->node_type == PT_VALUE && node->type_enum == PT_TYPE_INTEGER)
            {
              if (node->info.value.data_value.i == i)
            {
              pos_p->pos_no = i;

              if (referred_node != NULL)
                {
                  *referred_node = temp;
                }
            }
            }
        }
        }

      if (pos_p->pos_no != -1)
        {           /* found match */
          if (temp->type_enum != PT_TYPE_NONE && temp->type_enum != PT_TYPE_MAYBE)
        {       /* is resolved */
          pos_p->dom = pt_xasl_node_to_domain (parser, temp);
        }
          break;
        }

      i++;
    }

      /* if not found, second round search in select items with alias_print */
      if (pos_p->pos_no == -1 && node->alias_print != NULL)
    {

      for (i = 1, temp = root->info.query.q.select.list; temp != NULL; temp = temp->next, i++)
        {
          if (temp->alias_print == NULL)
        {
          continue;
        }

          if (pt_str_compare (node->alias_print, temp->alias_print, CASE_INSENSITIVE) == 0)
        {
          pos_p->pos_no = i;
          break;
        }
        }

      if (pos_p->pos_no != -1)
        {           /* found match */
          if (temp->type_enum != PT_TYPE_NONE && temp->type_enum != PT_TYPE_MAYBE)
        {       /* is resolved */
          pos_p->dom = pt_xasl_node_to_domain (parser, temp);
        }
        }
    }

      break;

    case PT_UNION:
    case PT_INTERSECTION:
    case PT_DIFFERENCE:
      pt_to_pos_descr (parser, pos_p, node, root->info.query.q.union_.arg1, referred_node, for_min_max_optimize);
      break;

    default:
      /* an error */
      break;
    }

  if (pos_p->pos_no == -1 || pos_p->dom == NULL)
    {               /* an error */
      pos_p->pos_no = -1;
      pos_p->dom = NULL;
    }
}


/*
 * pt_to_sort_list () - Translate a list of PT_SORT_SPEC nodes
 *                      to SORT_LIST list
 *   return:
 *   parser(in):
 *   node_list(in):
 *   col_list(in):
 *   sort_mode(in):
 */
static SORT_LIST *
pt_to_sort_list (PARSER_CONTEXT * parser, PT_NODE * node_list, PT_NODE * col_list, SORT_LIST_MODE sort_mode)
{
  SORT_LIST *sort_list, *sort, *lastsort;
  PT_NODE *node, *expr, *col;
  int i, k;
  int adjust_for_hidden_col_from = -1;
  DB_TYPE dom_type;
  bool is_analytic_window = false;

  sort_list = sort = lastsort = NULL;
  i = 0;            /* SORT_LIST pos_no start from 0 */

  if (sort_mode == SORT_LIST_ANALYTIC_WINDOW)
    {
      /* analytic sort specs behave just as ORDER BY sort specs, but error messages differ */
      sort_mode = SORT_LIST_ORDERBY;
      is_analytic_window = true;
    }

  /* check if a hidden column is in the select list; if it is the case, store the position in
   * 'adjust_for_hidden_col_from' - index starting from 1 !! Only one column is supported! If we deal with more than
   * two columns then we deal with SELECT ... FOR UPDATE and we must skip the check This adjustement is needed for
   * UPDATE statements with SELECT subqueries, executed on broker (ex: on tables with triggers); in this case, the
   * class OID field in select list is marked as hidden, and the coresponding sort value is skipped in
   * 'qdata_get_valptr_type_list', but the sorting position is not adjusted - this code anticipates the problem */
  if (sort_mode == SORT_LIST_ORDERBY)
    {
      for (col = col_list, k = 1; col && adjust_for_hidden_col_from != -2; col = col->next, k++)
    {
      switch (col->node_type)
        {
        case PT_FUNCTION:
          if (col->info.function.hidden_column && col->info.function.function_type == F_CLASS_OF)
        {
          if (adjust_for_hidden_col_from != -1)
            {
              adjust_for_hidden_col_from = -2;
            }
          else
            {
              adjust_for_hidden_col_from = k;
            }
          break;
        }
          break;

        case PT_NAME:
          if (col->info.name.hidden_column)
        {
          if (adjust_for_hidden_col_from != -1)
            {
              adjust_for_hidden_col_from = -2;
            }
          else
            {
              adjust_for_hidden_col_from = k;
            }
          break;
        }

        default:
          break;
        }
    }
    }

  for (node = node_list; node != NULL; node = node->next)
    {
      /* safe guard: invalid parse tree */
      if (node->node_type != PT_SORT_SPEC || (expr = node->info.sort_spec.expr) == NULL)
    {
      regu_set_error_with_zero_args (ER_REGU_SYSTEM);
      return NULL;
    }

      /* check for end-of-sort */
      if (node->info.sort_spec.pos_descr.pos_no <= 0)
    {
      if (sort_mode == SORT_LIST_AFTER_ISCAN || sort_mode == SORT_LIST_ORDERBY)
        {           /* internal error */
          if (!pt_has_error (parser))
        {
          PT_INTERNAL_ERROR (parser, "generate order_by");
        }
          return NULL;
        }
      else if (sort_mode == SORT_LIST_AFTER_GROUPBY)
        {
          /* i-th GROUP BY element does not appear in the select list. stop building sort_list */
          break;
        }
    }

      /* check for domain info */
      if (node->info.sort_spec.pos_descr.dom == NULL)
    {
      if (sort_mode == SORT_LIST_GROUPBY)
        {
          /* get domain from sort_spec node */
          if (expr->type_enum != PT_TYPE_NONE)
        {       /* is resolved */
          node->info.sort_spec.pos_descr.dom = pt_xasl_node_to_domain (parser, expr);
        }
        }
      else
        {
          /* get domain from corresponding column node */
          for (col = col_list, k = 1; col; col = col->next, k++)
        {
          if (node->info.sort_spec.pos_descr.pos_no == k)
            {
              break;    /* match */
            }
        }

          if (col && col->type_enum != PT_TYPE_NONE)
        {       /* is resolved */
          node->info.sort_spec.pos_descr.dom = pt_xasl_node_to_domain (parser, col);
        }
        }

      /* internal error */
      if (node->info.sort_spec.pos_descr.dom == NULL)
        {
          if (!pt_has_error (parser))
        {
          const char *sort_mode_str;

          if (sort_mode == SORT_LIST_AFTER_ISCAN)
            {
              sort_mode_str = "generate after_iscan";
            }
          else if (sort_mode == SORT_LIST_ORDERBY)
            {
              sort_mode_str = "generate order_by";
            }
          else if (sort_mode == SORT_LIST_GROUPBY)
            {
              sort_mode_str = "generate group_by";
            }
          else
            {
              sort_mode_str = "generate after_group_by";
            }

          PT_INTERNAL_ERROR (parser, sort_mode_str);
        }
          return NULL;
        }
    }

      /* GROUP BY ? or ORDER BY ? are not allowed */
      dom_type = TP_DOMAIN_TYPE (node->info.sort_spec.pos_descr.dom);

      if (is_analytic_window && (dom_type == DB_TYPE_BLOB || dom_type == DB_TYPE_CLOB || dom_type == DB_TYPE_VARIABLE))
    {
      /* analytic sort spec expressions have been moved to select list; check for host variable there */
      for (col = col_list, k = 1; col; col = col->next, k++)
        {
          if (node->info.sort_spec.pos_descr.pos_no == k)
        {
          break;
        }
        }

      if (col == NULL)
        {
          PT_INTERNAL_ERROR (parser, "sort spec out of bounds");
          return NULL;
        }

      if (col->node_type == PT_HOST_VAR || dom_type != DB_TYPE_VARIABLE)
        {
          PT_ERRORmf (parser, col, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_ALLOWED_IN_WINDOW,
              pt_short_print (parser, col));
          return NULL;
        }

      /* we allow variable domain but no host var */
    }
      else if (dom_type == DB_TYPE_BLOB || dom_type == DB_TYPE_CLOB
           || (node->info.sort_spec.expr->node_type == PT_HOST_VAR && dom_type == DB_TYPE_VARIABLE))
    {
      if (sort_mode == SORT_LIST_ORDERBY)
        {
          for (col = col_list, k = 1; col; col = col->next, k++)
        {
          if (node->info.sort_spec.pos_descr.pos_no == k)
            {
              break;
            }
        }
          PT_ERRORmf (parser, col, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NO_ORDERBY_ALLOWED,
              pt_short_print (parser, col));
          return NULL;
        }
      else if (sort_mode == SORT_LIST_GROUPBY)
        {
          PT_ERRORmf (parser, expr, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NO_GROUPBY_ALLOWED,
              pt_short_print (parser, expr));
          return NULL;
        }
    }

      regu_alloc (sort);
      if (!sort)
    {
      regu_set_error_with_zero_args (ER_REGU_SYSTEM);
      return NULL;
    }

      /* set values */
      sort->s_order = (node->info.sort_spec.asc_or_desc == PT_ASC) ? S_ASC : S_DESC;
      sort->s_nulls = pt_to_null_ordering (node);
      sort->pos_descr = node->info.sort_spec.pos_descr;

      /* PT_SORT_SPEC pos_no start from 1, SORT_LIST pos_no start from 0 */
      if (sort_mode == SORT_LIST_GROUPBY)
    {
      /* set i-th position */
      sort->pos_descr.pos_no = i++;
    }
      else
    {
      sort->pos_descr.pos_no--;
      if (adjust_for_hidden_col_from > -1)
        {
          assert (sort_mode == SORT_LIST_ORDERBY);
          /* adjust for hidden column */
          if (node->info.sort_spec.pos_descr.pos_no >= adjust_for_hidden_col_from)
        {
          sort->pos_descr.pos_no--;
          assert (sort->pos_descr.pos_no >= 0);
        }
        }
    }

      /* link up */
      if (sort_list)
    {
      lastsort->next = sort;
    }
      else
    {
      sort_list = sort;
    }

      lastsort = sort;
    }

  return sort_list;
}


/*
 * pt_to_after_iscan () - Translate a list of after iscan PT_SORT_SPEC nodes
 *                        to SORT_LIST list
 *   return:
 *   parser(in):
 *   iscan_list(in):
 *   root(in):
 */
static SORT_LIST *
pt_to_after_iscan (PARSER_CONTEXT * parser, PT_NODE * iscan_list, PT_NODE * root)
{
  return pt_to_sort_list (parser, iscan_list, pt_get_select_list (parser, root), SORT_LIST_AFTER_ISCAN);
}


/*
 * pt_to_orderby () - Translate a list of order by PT_SORT_SPEC nodes
 *                    to SORT_LIST list
 *   return:
 *   parser(in):
 *   order_list(in):
 *   root(in):
 */
SORT_LIST *
pt_to_orderby (PARSER_CONTEXT * parser, PT_NODE * order_list, PT_NODE * root)
{
  return pt_to_sort_list (parser, order_list, pt_get_select_list (parser, root), SORT_LIST_ORDERBY);
}


/*
 * pt_to_groupby () - Translate a list of group by PT_SORT_SPEC nodes
 *                    to SORT_LIST list.(ALL ascending)
 *   return:
 *   parser(in):
 *   group_list(in):
 *   root(in):
 */
static SORT_LIST *
pt_to_groupby (PARSER_CONTEXT * parser, PT_NODE * group_list, PT_NODE * root)
{
  return pt_to_sort_list (parser, group_list, pt_get_select_list (parser, root), SORT_LIST_GROUPBY);
}


/*
 * pt_to_after_groupby () - Translate a list of after group by PT_SORT_SPEC
 *                          nodes to SORT_LIST list.(ALL ascending)
 *   return:
 *   parser(in):
 *   group_list(in):
 *   root(in):
 */
static SORT_LIST *
pt_to_after_groupby (PARSER_CONTEXT * parser, PT_NODE * group_list, PT_NODE * root)
{
  return pt_to_sort_list (parser, group_list, pt_get_select_list (parser, root), SORT_LIST_AFTER_GROUPBY);
}


/*
 * pt_to_pred_terms () -
 *   return:
 *   parser(in):
 *   terms(in): CNF tree
 *   id(in): spec id to test term for
 *   pred(in):
 */
static void
pt_to_pred_terms (PARSER_CONTEXT * parser, PT_NODE * terms, UINTPTR id, PRED_EXPR ** pred)
{
  PRED_EXPR *pred1;
  PT_NODE *next;

  while (terms)
    {
      /* break links, they are a short-hand for 'AND' in CNF terms */
      next = terms->next;
      terms->next = NULL;

      if (terms->node_type == PT_EXPR && terms->info.expr.op == PT_AND)
    {
      pt_to_pred_terms (parser, terms->info.expr.arg1, id, pred);
      pt_to_pred_terms (parser, terms->info.expr.arg2, id, pred);
    }
      else
    {
      if (terms->spec_ident == (UINTPTR) id)
        {
          pred1 = pt_to_pred_expr (parser, terms);
          if (!*pred)
        {
          *pred = pred1;
        }
          else
        {
          *pred = pt_make_pred_expr_pred (pred1, *pred, B_AND);
        }
        }
    }

      /* repair link */
      terms->next = next;
      terms = next;
    }
}


/*
 * regu_make_constant_vid () - convert a vmop into a db_value
 *   return: NO_ERROR on success, non-zero for ERROR
 *   val(in): a virtual object instance
 *   dbvalptr(out): pointer to a db_value
 */
static int
regu_make_constant_vid (DB_VALUE * val, DB_VALUE ** dbvalptr)
{
  DB_OBJECT *vmop, *cls, *proxy, *real_instance;
  DB_VALUE *keys = NULL, *virt_val, *proxy_val;
  OID virt_oid, proxy_oid;
  DB_IDENTIFIER *dbid;
  DB_SEQ *seq;
  int is_vclass = 0;

  assert (val != NULL);

  /* make sure we got a virtual MOP and a db_value */
  if (DB_VALUE_TYPE (val) != DB_TYPE_OBJECT || !(vmop = db_get_object (val)) || !WS_ISVID (vmop))
    {
      return ER_GENERIC_ERROR;
    }

  regu_alloc (*dbvalptr);
  regu_alloc (virt_val);
  regu_alloc (proxy_val);
  regu_alloc (keys);
  if (*dbvalptr == NULL || virt_val == NULL || proxy_val == NULL || keys == NULL)
    {
      return ER_GENERIC_ERROR;
    }

  /* compute vmop's three canonical values: virt, proxy, keys */
  cls = db_get_class (vmop);
  is_vclass = db_is_vclass (cls);
  if (is_vclass < 0)
    {
      return is_vclass;
    }
  if (!is_vclass)
    {
      OID_SET_NULL (&virt_oid);
      real_instance = vmop;
      OID_SET_NULL (&proxy_oid);
      *keys = *val;
    }
  else
    {
      /* make sure its oid is a good one */
      dbid = ws_identifier (cls);
      if (!dbid)
    {
      return ER_GENERIC_ERROR;
    }

      virt_oid = *dbid;
      real_instance = db_real_instance (vmop);
      if (!real_instance)
    {
      OID_SET_NULL (&proxy_oid);
      vid_get_keys (vmop, keys);
    }
      else
    {
      proxy = db_get_class (real_instance);
      OID_SET_NULL (&proxy_oid);
      vid_get_keys (vmop, keys);
    }
    }

  db_make_oid (virt_val, &virt_oid);
  db_make_oid (proxy_val, &proxy_oid);

  /* the DB_VALUE form of a VMOP is given a type of DB_TYPE_VOBJ and takes the form of a 3-element sequence: virt,
   * proxy, keys (Oh what joy to find out the secret encoding of a virtual object!) */
  if ((seq = db_seq_create (NULL, NULL, 3)) == NULL)
    {
      goto error_cleanup;
    }

  if (db_seq_put (seq, 0, virt_val) != NO_ERROR)
    {
      goto error_cleanup;
    }

  if (db_seq_put (seq, 1, proxy_val) != NO_ERROR)
    {
      goto error_cleanup;
    }

  /* this may be a nested sequence, so turn on nested sets */
  if (db_seq_put (seq, 2, keys) != NO_ERROR)
    {
      goto error_cleanup;
    }

  db_make_sequence (*dbvalptr, seq);
  db_value_alter_type (*dbvalptr, DB_TYPE_VOBJ);

  return NO_ERROR;

error_cleanup:
  pr_clear_value (keys);
  return ER_GENERIC_ERROR;
}

/*
 * set_has_objs () - set dbvalptr to the DB_VALUE form of val
 *   return: nonzero if set has some objs, zero otherwise
 *   seq(in): a set/seq db_value
 */
static int
set_has_objs (DB_SET * seq)
{
  int found = 0, i, siz;
  DB_VALUE elem;

  siz = db_seq_size (seq);
  for (i = 0; i < siz && !found; i++)
    {
      if (db_set_get (seq, i, &elem) < 0)
    {
      return 0;
    }

      if (DB_VALUE_DOMAIN_TYPE (&elem) == DB_TYPE_OBJECT)
    {
      found = 1;
    }

      db_value_clear (&elem);
    }

  return found;
}

/*
 * setof_mop_to_setof_vobj () - creates & fill new set/seq with converted
 *                              vobj elements of val
 *   return: NO_ERROR on success, non-zero for ERROR
 *   parser(in):
 *   seq(in): a set/seq of mop-bearing elements
 *   new_val(out):
 */
static int
setof_mop_to_setof_vobj (PARSER_CONTEXT * parser, DB_SET * seq, DB_VALUE * new_val)
{
  int i, siz;
  DB_VALUE elem, *new_elem;
  DB_SET *new_set;
  DB_OBJECT *obj;
  OID *oid;
  DB_TYPE typ;

  /* make sure we got a set/seq */
  typ = db_set_type (seq);
  if (!pr_is_set_type (typ))
    {
      goto failure;
    }

  /* create a new set/seq */
  siz = db_seq_size (seq);
  if (typ == DB_TYPE_SET)
    {
      new_set = db_set_create_basic (NULL, NULL);
    }
  else if (typ == DB_TYPE_MULTISET)
    {
      new_set = db_set_create_multi (NULL, NULL);
    }
  else
    {
      new_set = db_seq_create (NULL, NULL, siz);
    }

  /* fill the new_set with the vobj form of val's mops */
  for (i = 0; i < siz; i++)
    {
      if (db_set_get (seq, i, &elem) < 0)
    {
      goto failure;
    }

      if (DB_IS_NULL (&elem))
    {
      regu_alloc (new_elem);
      if (!new_elem)
        {
          goto failure;
        }
      db_value_domain_init (new_elem, DB_TYPE_OBJECT, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
    }
      else if (DB_VALUE_DOMAIN_TYPE (&elem) != DB_TYPE_OBJECT || (obj = db_get_object (&elem)) == NULL)
    {
      /* the set has mixed object and non-object types. */
      new_elem = &elem;
    }
      else
    {
      /* convert val's mop into a vobj */
      if (WS_ISVID (obj))
        {
          if (regu_make_constant_vid (&elem, &new_elem) != NO_ERROR)
        {
          goto failure;
        }

          /* we need to register the constant vid as an orphaned db_value that the parser should free later.  We
           * can't free it until after the xasl has been packed. */
          pt_register_orphan_db_value (parser, new_elem);
        }
      else
        {
          regu_alloc (new_elem);
          if (!new_elem)
        {
          goto failure;
        }

          if (WS_IS_DELETED (obj))
        {
          db_value_domain_init (new_elem, DB_TYPE_OBJECT, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
        }
          else
        {
          oid = db_identifier (obj);
          if (oid == NULL)
            {
              if (er_errid () == ER_HEAP_UNKNOWN_OBJECT)
            {
              db_value_domain_init (new_elem, DB_TYPE_OBJECT, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE);
            }
              else
            {
              goto failure;
            }
            }
          else
            {
              db_make_object (new_elem, ws_mop (oid, NULL));
            }
        }
        }
    }

      /* stuff the vobj form of the mop into new_set */
      if (typ == DB_TYPE_SET || typ == DB_TYPE_MULTISET)
    {
      if (db_set_add (new_set, new_elem) < 0)
        {
          goto failure;
        }
    }
      else if (db_seq_put (new_set, i, new_elem) < 0)
    {
      goto failure;
    }

      db_value_clear (&elem);
    }

  /* stuff new_set into new_val */
  if (typ == DB_TYPE_SET)
    {
      db_make_set (new_val, new_set);
    }
  else if (typ == DB_TYPE_MULTISET)
    {
      db_make_multiset (new_val, new_set);
    }
  else
    {
      db_make_sequence (new_val, new_set);
    }

  return NO_ERROR;

failure:
  PT_INTERNAL_ERROR (parser, "generate var");
  return ER_FAILED;
}


/*
 * pt_make_regu_hostvar () - takes a pt_node of host variable and make
 *                           a regu_variable of host variable reference
 *   return:
 *   parser(in/out):
 *   node(in):
 */
static REGU_VARIABLE *
pt_make_regu_hostvar (PARSER_CONTEXT * parser, const PT_NODE * node)
{
  REGU_VARIABLE *regu;
  DB_VALUE *val;
  DB_TYPE typ, exptyp;

  regu_alloc (regu);
  if (regu)
    {
      val = &parser->host_variables[node->info.host_var.index];
      typ = DB_VALUE_DOMAIN_TYPE (val);

      regu->type = TYPE_POS_VALUE;
      regu->value.val_pos = node->info.host_var.index;
      if (parser->dbval_cnt <= node->info.host_var.index)
    {
      parser->dbval_cnt = node->info.host_var.index + 1;
    }

      /* determine the domain of this host var */
      regu->domain = NULL;

      if (node->data_type)
    {
      /* try to get domain info from its data_type */
      regu->domain = pt_xasl_node_to_domain (parser, node);
    }

      if (regu->domain == NULL && (parser->flag.set_host_var == 1 || typ != DB_TYPE_NULL))
    {
      /* if the host var DB_VALUE was initialized before, use its domain for regu variable */
      TP_DOMAIN *domain;
      if (TP_IS_CHAR_TYPE (typ))
        {
          domain = pt_xasl_type_enum_to_domain (pt_db_to_type_enum (typ));
          regu->domain = tp_domain_copy (domain, false);
          if (regu->domain != NULL)
        {
          regu->domain->codeset = db_get_string_codeset (val);
          regu->domain->collation_id = db_get_string_collation (val);
          regu->domain->precision = db_value_precision (val);
          regu->domain->scale = db_value_scale (val);
          regu->domain = tp_domain_cache (regu->domain);
          if (regu->domain == NULL)
            {
              goto error_exit;
            }
        }
          else
        {
          goto error_exit;
        }
        }
      else
        {
          regu->domain = pt_xasl_type_enum_to_domain (pt_db_to_type_enum (typ));
        }
    }

      if (regu->domain == NULL && node->expected_domain)
    {
      /* try to get domain infor from its expected_domain */
      regu->domain = node->expected_domain;
    }

      if (regu->domain == NULL)
    {
      /* try to get domain info from its type_enum */
      regu->domain = pt_xasl_type_enum_to_domain (node->type_enum);
    }

      if (regu->domain == NULL)
    {
      PT_INTERNAL_ERROR (parser, "unresolved data type of host var");
      regu = NULL;
    }
      else
    {
      exptyp = TP_DOMAIN_TYPE (regu->domain);
      if (parser->flag.set_host_var == 0 && typ == DB_TYPE_NULL)
        {
          /* If the host variable was not given before by the user, preset it by the expected domain. When the user
           * set the host variable, its value will be casted to this domain if necessary. */
          (void) db_value_domain_init (val, exptyp, regu->domain->precision, regu->domain->scale);
          if (TP_IS_CHAR_TYPE (exptyp))
        {
          db_string_put_cs_and_collation (val, TP_DOMAIN_CODESET (regu->domain),
                          TP_DOMAIN_COLLATION (regu->domain));
        }
        }
      else if (typ != exptyp
           || (TP_TYPE_HAS_COLLATION (typ) && TP_TYPE_HAS_COLLATION (exptyp)
               && (db_get_string_collation (val) != TP_DOMAIN_COLLATION (regu->domain))))
        {
          if (tp_value_cast (val, val, regu->domain, false) != DOMAIN_COMPATIBLE)
        {
          PT_ERRORmf2 (parser, node, MSGCAT_SET_ERROR, -(ER_TP_CANT_COERCE),
                   pr_type_name (DB_VALUE_DOMAIN_TYPE (val)), pr_type_name (TP_DOMAIN_TYPE (regu->domain)));
          regu = NULL;
        }
        }
    }
    }
  else
    {
      regu = NULL;
    }

  return regu;

error_exit:
  return NULL;
}

/*
 * pt_make_regu_reguvalues_list () - takes a pt_node of host variable and make
 *                                   a regu_variable of value list reference
 *   return: A NULL return indicates an error occurred
 *   parser(in):
 *   node(in):
 */
static REGU_VARIABLE *
pt_make_regu_reguvalues_list (PARSER_CONTEXT * parser, const PT_NODE * node, UNBOX unbox)
{
  REGU_VARIABLE *regu = NULL;
  REGU_VALUE_LIST *regu_list = NULL;
  REGU_VALUE_ITEM *list_node = NULL;
  PT_NODE *temp = NULL;

  assert (node);

  regu_alloc (regu);
  if (regu)
    {
      regu->type = TYPE_REGUVAL_LIST;

      regu_alloc (regu_list);
      if (regu_list == NULL)
    {
      return NULL;
    }
      regu->value.reguval_list = regu_list;

      for (temp = node->info.node_list.list; temp; temp = temp->next_row)
    {
      regu_alloc (list_node);
      if (list_node == NULL)
        {
          return NULL;
        }

      if (regu_list->current_value == NULL)
        {
          regu_list->regu_list = list_node;
        }
      else
        {
          regu_list->current_value->next = list_node;
        }

      regu_list->current_value = list_node;
      list_node->value = pt_to_regu_variable (parser, temp, unbox);
      if (list_node->value == NULL)
        {
          return NULL;
        }
      regu_list->count += 1;
    }
      regu_list->current_value = regu_list->regu_list;
      regu->domain = regu_list->regu_list->value->domain;
    }

  return regu;
}

/*
 * pt_make_regu_constant () - takes a db_value and db_type and makes
 *                            a regu_variable constant
 *   return: A NULL return indicates an error occurred
 *   parser(in):
 *   db_value(in/out):
 *   db_type(in):
 *   node(in):
 */
static REGU_VARIABLE *
pt_make_regu_constant (PARSER_CONTEXT * parser, DB_VALUE * db_value, const DB_TYPE db_type, const PT_NODE * node)
{
  REGU_VARIABLE *regu = NULL;
  DB_VALUE *dbvalptr = NULL;
  DB_VALUE tmp_val;
  DB_TYPE typ;
  int is_null;
  DB_SET *set = NULL;

  db_make_null (&tmp_val);
  if (db_value)
    {
      regu_alloc (regu);
      if (regu)
    {
      if (node)
        {
          regu->domain = pt_xasl_node_to_domain (parser, node);
        }
      else
        {
          /* just use the type to create the domain, this is a special case */
          regu->domain = tp_domain_resolve_default (db_type);
        }

      regu->type = TYPE_CONSTANT;
      typ = DB_VALUE_DOMAIN_TYPE (db_value);
      is_null = DB_IS_NULL (db_value);
      if (is_null)
        {
          regu->value.dbvalptr = db_value;
        }
      else if (typ == DB_TYPE_OBJECT)
        {
          if (db_get_object (db_value) && WS_ISVID (db_get_object (db_value)))
        {
          if (regu_make_constant_vid (db_value, &dbvalptr) != NO_ERROR)
            {
              return NULL;
            }
          else
            {
              regu->value.dbvalptr = dbvalptr;
              regu->domain = &tp_Vobj_domain;

              /* we need to register the constant vid as an orphaned db_value that the parser should free
               * later. We can't free it until after the xasl has been packed. */
              pt_register_orphan_db_value (parser, dbvalptr);
            }
        }
          else
        {
          OID *oid;

          oid = db_identifier (db_get_object (db_value));
          if (oid == NULL)
            {
              db_value_put_null (db_value);
            }
          else
            {
              db_make_object (db_value, ws_mop (oid, NULL));
            }
          regu->value.dbvalptr = db_value;
        }
        }
      else if (pr_is_set_type (typ) && (set = db_get_set (db_value)) != NULL && set_has_objs (set))
        {
          if (setof_mop_to_setof_vobj (parser, set, &tmp_val) != NO_ERROR)
        {
          return NULL;
        }
          regu->value.dbvalptr = &tmp_val;
        }
      else
        {
          regu->value.dbvalptr = db_value;
        }

      /* db_value may be in a pt_node that will be freed before mapping the xasl to a stream. This makes sure that
       * we have captured the contents of the variable. It also uses the in-line db_value of a regu variable,
       * saving xasl space. */
      db_value = regu->value.dbvalptr;
      regu->value.dbvalptr = NULL;
      regu->type = TYPE_DBVAL;
      db_value_clone (db_value, &regu->value.dbval);

      /* we need to register the dbvalue within the regu constant as an orphan that the parser should free later.
       * We can't free it until after the xasl has been packed. */
      pt_register_orphan_db_value (parser, &regu->value.dbval);

      /* if setof_mop_to_setof_vobj() was called, then a new set was created.  The dbvalue needs to be cleared. */
      pr_clear_value (&tmp_val);
    }
    }

  return regu;
}


/*
 * pt_make_regu_arith () - takes a regu_variable pair,
 *                         and makes an regu arith type
 *   return: A NULL return indicates an error occurred
 *   arg1(in):
 *   arg2(in):
 *   arg3(in):
 *   op(in):
 *   domain(in):
 */
REGU_VARIABLE *
pt_make_regu_arith (const REGU_VARIABLE * arg1, const REGU_VARIABLE * arg2, const REGU_VARIABLE * arg3,
            const OPERATOR_TYPE op, const TP_DOMAIN * domain)
{
  REGU_VARIABLE *regu = NULL;
  ARITH_TYPE *arith;
  DB_VALUE *dbval;

  if (domain == NULL)
    {
      return NULL;
    }

  regu_alloc (arith);
  regu_alloc (dbval);
  regu_alloc (regu);

  if (arith == NULL || dbval == NULL || regu == NULL)
    {
      return NULL;
    }

  regu_dbval_type_init (dbval, TP_DOMAIN_TYPE (domain));
  arith->domain = (TP_DOMAIN *) domain;
  arith->value = dbval;
  arith->opcode = op;
  arith->leftptr = (REGU_VARIABLE *) arg1;
  arith->rightptr = (REGU_VARIABLE *) arg2;
  arith->thirdptr = (REGU_VARIABLE *) arg3;
  arith->pred = NULL;
  arith->rand_seed = NULL;
  regu->type = TYPE_INARITH;
  regu->value.arithptr = arith;

  return regu;
}

/*
 * pt_make_regu_pred () - takes a pred expr and makes a special arith
 *            regu variable, with T_PREDICATE as opcode,
 *            that holds the predicate expression.
 *
 *   return: A NULL return indicates an error occurred
 *   pred(in):
 */
static REGU_VARIABLE *
pt_make_regu_pred (const PRED_EXPR * pred)
{
  REGU_VARIABLE *regu = NULL;
  ARITH_TYPE *arith = NULL;
  DB_VALUE *dbval = NULL;
  TP_DOMAIN *domain = NULL;

  if (pred == NULL)
    {
      return NULL;
    }

  regu_alloc (arith);
  regu_alloc (dbval);
  regu_alloc (regu);

  if (arith == NULL || dbval == NULL || regu == NULL)
    {
      return NULL;
    }

  domain = tp_domain_resolve_default (DB_TYPE_INTEGER);
  if (domain == NULL)
    {
      return NULL;
    }
  regu->domain = domain;
  regu_dbval_type_init (dbval, TP_DOMAIN_TYPE (domain));
  arith->domain = (TP_DOMAIN *) domain;
  arith->value = dbval;
  arith->opcode = T_PREDICATE;
  arith->leftptr = NULL;
  arith->rightptr = NULL;
  arith->thirdptr = NULL;
  arith->pred = (PRED_EXPR *) pred;
  arith->rand_seed = NULL;
  regu->type = TYPE_INARITH;
  regu->value.arithptr = arith;

  return regu;
}

/*
 * pt_make_vid () - takes a pt_data_type and a regu variable and makes
 *                  a regu vid function
 *   return: A NULL return indicates an error occurred
 *   parser(in):
 *   data_type(in):
 *   regu3(in):
 */
static REGU_VARIABLE *
pt_make_vid (PARSER_CONTEXT * parser, const PT_NODE * data_type, const REGU_VARIABLE * regu3)
{
  REGU_VARIABLE *regu = NULL;
  REGU_VARIABLE *regu1 = NULL;
  REGU_VARIABLE *regu2 = NULL;
  DB_VALUE *value1, *value2;
  DB_OBJECT *virt;
  OID virt_oid, proxy_oid;
  DB_IDENTIFIER *dbid;

  if (!data_type || !regu3)
    {
      return NULL;
    }

  virt = data_type->info.data_type.virt_object;
  if (virt)
    {
      /* make sure its oid is a good one */
      dbid = db_identifier (virt);
      if (!dbid)
    {
      return NULL;
    }
      virt_oid = *dbid;
    }
  else
    {
      OID_SET_NULL (&virt_oid);
    }

  OID_SET_NULL (&proxy_oid);

  regu_alloc (value1);
  regu_alloc (value2);
  if (!value1 || !value2)
    {
      return NULL;
    }

  db_make_oid (value1, &virt_oid);
  db_make_oid (value2, &proxy_oid);

  regu1 = pt_make_regu_constant (parser, value1, DB_TYPE_OID, NULL);
  regu2 = pt_make_regu_constant (parser, value2, DB_TYPE_OID, NULL);
  if (!regu1 || !regu2)
    {
      return NULL;
    }

  regu_alloc (regu);
  if (!regu)
    {
      PT_ERROR (parser, data_type,
        msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      return NULL;
    }

  regu->type = TYPE_FUNC;

  /* we just use the standard vanilla vobj domain */
  regu->domain = &tp_Vobj_domain;
  regu_alloc (regu->value.funcp);
  if (!regu->value.funcp)
    {
      PT_ERROR (parser, data_type,
        msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      return NULL;
    }

  regu->value.funcp->ftype = F_VID;
  regu_alloc (regu->value.funcp->operand);
  if (!regu->value.funcp->operand)
    {
      PT_ERROR (parser, data_type,
        msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      return NULL;
    }

  regu->value.funcp->operand->value = *regu1;
  regu_alloc (regu->value.funcp->operand->next);
  if (!regu->value.funcp->operand->next)
    {
      PT_ERROR (parser, data_type,
        msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      return NULL;
    }

  regu->value.funcp->operand->next->value = *regu2;
  regu_alloc (regu->value.funcp->operand->next->next);
  if (!regu->value.funcp->operand->next->next)
    {
      PT_ERROR (parser, data_type,
        msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      return NULL;
    }

  regu->value.funcp->operand->next->next->value = *regu3;
  regu->value.funcp->operand->next->next->next = NULL;

  regu->flags = regu3->flags;

  regu_dbval_type_init (regu->value.funcp->value, DB_TYPE_VOBJ);

  return regu;
}


/*
 * pt_make_function () - takes a pt_data_type and a regu variable and makes
 *                       a regu function
 *   return: A NULL return indicates an error occurred
 *   parser(in):
 *   function_code(in):
 *   arg_list(in):
 *   result_type(in):
 *   node(in):
 */
static REGU_VARIABLE *
pt_make_function (PARSER_CONTEXT * parser, int function_code, const REGU_VARIABLE_LIST arg_list,
          const DB_TYPE result_type, const PT_NODE * node)
{
  REGU_VARIABLE *regu;
  TP_DOMAIN *domain;

  regu_alloc (regu);
  if (!regu)
    {
      return NULL;
    }

  domain = pt_xasl_node_to_domain (parser, node);
  regu->type = TYPE_FUNC;
  regu->domain = domain;
  regu_alloc (regu->value.funcp);

  if (regu->value.funcp)
    {
      regu->value.funcp->operand = arg_list;
      regu->value.funcp->ftype = (FUNC_CODE) function_code;
      if (node->info.function.hidden_column)
    {
      REGU_VARIABLE_SET_FLAG (regu, REGU_VARIABLE_HIDDEN_COLUMN);
    }

      regu_dbval_type_init (regu->value.funcp->value, result_type);
      regu->value.funcp->tmp_obj = NULL;
    }

  return regu;
}


/*
 * pt_stored_procedure_to_regu () - takes a PT_METHOD_CALL and converts to a regu_variable
 *   return: A NULL return indicates an error occurred
 *   parser(in):
 *   function(in/out):
 *
 */
static REGU_VARIABLE *
pt_stored_procedure_to_regu (PARSER_CONTEXT * parser, PT_NODE * node)
{
  int error = NO_ERROR;
  REGU_VARIABLE *regu = NULL;
  REGU_VARIABLE_LIST args;
  SP_TYPE *sp = NULL;

  regu_alloc (regu);
  if (!regu)
    {
      return NULL;
    }

  regu_alloc (regu->value.sp_ptr);

  sp = regu->value.sp_ptr;
  if (sp)
    {
      error = jsp_make_pl_signature (parser, node, NULL, *(sp->sig));
      if (error != NO_ERROR)
    {
      return NULL;
    }

      PT_NODE *default_next_node_list = jsp_get_default_expr_node_list (parser, *(sp->sig));
      node->info.method_call.arg_list = parser_append_node (default_next_node_list, node->info.method_call.arg_list);

      DB_TYPE result_type = (DB_TYPE) sp->sig->result_type;
      if (result_type == DB_TYPE_RESULTSET)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_SP_CANNOT_RETURN_RESULTSET, 0);
      return NULL;
    }

      regu_dbval_type_init (sp->value, result_type);
      sp->args = pt_to_regu_variable_list (parser, node->info.method_call.arg_list, UNBOX_AS_VALUE, NULL, NULL);
    }

  regu->type = TYPE_SP;

  if (node->type_enum != PT_TYPE_NUMERIC)
    {
      regu->domain = pt_xasl_node_to_domain (parser, node);
    }
  else
    {
      /*
       * To avoid being set to default Numeric, set numeric(any,any) to precision = 0, scale = 0.
       * TO DO: We need to define a separate type for numeric(any,any) in the future.
       */
      int *numeric = prm_get_integer_list_value (PRM_ID_STORED_PROCEDURE_RETURN_NUMERIC_SIZE);

      regu->domain = pt_node_to_db_domain (parser, node, NULL);
      regu->domain->precision = numeric[PRM_PRECISION];
      regu->domain->scale = numeric[PRM_SCALE];
    }

  return regu;
}

/*
 * pt_function_to_regu () - takes a PT_FUNCTION and converts to a regu_variable
 *   return: A NULL return indicates an error occurred
 *   parser(in):
 *   function(in/out):
 *
 * Note :
 * currently only aggregate functions are known and handled
 */
static REGU_VARIABLE *
pt_function_to_regu (PARSER_CONTEXT * parser, PT_NODE * function)
{
  REGU_VARIABLE *regu = NULL;
  DB_VALUE *dbval;
  bool is_aggregate, is_analytic;
  REGU_VARIABLE_LIST args;
  DB_TYPE result_type = DB_TYPE_SET;

  is_aggregate = pt_is_aggregate_function (parser, function);
  is_analytic = function->info.function.analytic.is_analytic;

  if (is_aggregate || is_analytic)
    {
      /* This procedure assumes that pt_to_aggregate () / pt_to_analytic () has already run, setting up the DB_VALUE
       * for the aggregate value. */
      dbval = (DB_VALUE *) function->etc;
      if (dbval)
    {
      regu_alloc (regu);
      if (regu)
        {
          regu->type = TYPE_CONSTANT;
          regu->domain = pt_xasl_node_to_domain (parser, function);
          regu->value.dbvalptr = dbval;
        }
      else
        {
          PT_ERROR (parser, function,
            msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC,
                    MSGCAT_SEMANTIC_OUT_OF_MEMORY));
          return NULL;
        }
    }
      else
    {
      PT_ERRORm (parser, function, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_NESTED_AGGREGATE);
    }
    }
  else
    {
      /* change the generic code to the server side generic code */
      if (function->info.function.function_type == PT_GENERIC)
    {
      function->info.function.function_type = F_GENERIC;
    }

      if (function->info.function.function_type < F_TOP_TABLE_FUNC)
    {
      args = pt_to_regu_variable_list (parser, function->info.function.arg_list, UNBOX_AS_TABLE, NULL, NULL);
    }
      else
    {
      args = pt_to_regu_variable_list (parser, function->info.function.arg_list, UNBOX_AS_VALUE, NULL, NULL);
    }

      switch (function->info.function.function_type)
    {
    case F_SET:
    case F_TABLE_SET:
      result_type = DB_TYPE_SET;
      break;
    case F_MULTISET:
    case F_TABLE_MULTISET:
      result_type = DB_TYPE_MULTISET;
      break;
    case F_SEQUENCE:
    case F_TABLE_SEQUENCE:
      result_type = DB_TYPE_SEQUENCE;
      break;
    case F_MIDXKEY:
      result_type = DB_TYPE_MIDXKEY;
      break;
    case F_VID:
      result_type = DB_TYPE_VOBJ;
      break;
    case F_GENERIC:
      result_type = pt_node_to_db_type (function);
      break;
    case F_CLASS_OF:
      result_type = DB_TYPE_OID;
      break;
    case F_INSERT_SUBSTRING:
    case F_ELT:
    case F_REGEXP_COUNT:
    case F_REGEXP_INSTR:
    case F_REGEXP_LIKE:
    case F_REGEXP_REPLACE:
    case F_REGEXP_SUBSTR:
      result_type = pt_node_to_db_type (function);
      break;
    case F_BENCHMARK:
    case F_JSON_ARRAY:
    case F_JSON_ARRAY_APPEND:
    case F_JSON_ARRAY_INSERT:
    case F_JSON_CONTAINS:
    case F_JSON_CONTAINS_PATH:
    case F_JSON_DEPTH:
    case F_JSON_EXTRACT:
    case F_JSON_GET_ALL_PATHS:
    case F_JSON_KEYS:
    case F_JSON_INSERT:
    case F_JSON_LENGTH:
    case F_JSON_MERGE:
    case F_JSON_MERGE_PATCH:
    case F_JSON_OBJECT:
    case F_JSON_PRETTY:
    case F_JSON_QUOTE:
    case F_JSON_REMOVE:
    case F_JSON_REPLACE:
    case F_JSON_SEARCH:
    case F_JSON_SET:
    case F_JSON_TYPE:
    case F_JSON_UNQUOTE:
    case F_JSON_VALID:
      result_type = pt_node_to_db_type (function);
      break;
    default:
      PT_ERRORf (parser, function, "Internal error in generate(%d)", __LINE__);
    }

      if (args)
    {
      regu = pt_make_function (parser, function->info.function.function_type, args, result_type, function);
      if (DB_TYPE_VOBJ == pt_node_to_db_type (function) && function->info.function.function_type != F_VID)
        {
          regu = pt_make_vid (parser, function->data_type, regu);
        }
    }
    }

  return regu;
}

/*
 * pt_make_regu_subquery () - Creates a regu variable that executes a
 *                sub-query and stores its results.
 *
 * return      : Pointer to generated regu variable.
 * parser (in) : Parser context.
 * xasl (in)   : XASL node for sub-query.
 * unbox (in)  : UNBOX value (as table or as value).
 * node (in)   : Parse tree node for sub-query.
 */
static REGU_VARIABLE *
pt_make_regu_subquery (PARSER_CONTEXT * parser, XASL_NODE * xasl, const UNBOX unbox, const PT_NODE * node)
{
  REGU_VARIABLE *regu = NULL;
  QFILE_SORTED_LIST_ID *srlist_id = NULL;

  if (xasl)
    {
      regu_alloc (regu);
      if (!regu)
    {
      return NULL;
    }

      regu->domain = pt_xasl_node_to_domain (parser, node);

      /* set as linked to regu var */
      XASL_SET_FLAG (xasl, XASL_LINK_TO_REGU_VARIABLE);
      regu->xasl = xasl;

      xasl->is_single_tuple = (unbox != UNBOX_AS_TABLE);
      if (xasl->is_single_tuple)
    {
      if (!xasl->single_tuple)
        {
          xasl->single_tuple = pt_make_val_list (parser, (PT_NODE *) node);
        }

      if (xasl->single_tuple)
        {
          regu->type = TYPE_CONSTANT;
          regu->value.dbvalptr = xasl->single_tuple->valp->val;
        }
      else
        {
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          regu = NULL;
        }

      if (pt_prepare_corr_subquery_hash_result_cache (parser, (PT_NODE *) node, xasl))
        {
          XASL_SET_FLAG (xasl, XASL_USES_SQ_CACHE);
        }
    }
      else
    {
      regu_alloc (srlist_id);
      if (srlist_id)
        {
          regu->type = TYPE_LIST_ID;
          regu->value.srlist_id = srlist_id;
          srlist_id->list_id = xasl->list_id;
        }
      else
        {
          regu = NULL;
        }
    }
    }

  return regu;
}

/*
 * pt_make_regu_insert () - Creates a regu variable that executes an insert
 *              statement and stored the OID of inserted object.
 *
 * return     : Pointer to generated regu variable.
 * parser (in)    : Parser context.
 * statement (in) : Parse tree node for insert statement.
 */
static REGU_VARIABLE *
pt_make_regu_insert (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  XASL_NODE *xasl = NULL;
  REGU_VARIABLE *regu = NULL;

  if (statement == NULL || statement->node_type != PT_INSERT)
    {
      assert (false);
      return NULL;
    }

  /* Generate xasl for insert statement */
  xasl = pt_to_insert_xasl (parser, statement);
  if (xasl == NULL)
    {
      return NULL;
    }

  /* Create the value to store the inserted object */
  xasl->proc.insert.obj_oid = db_value_create ();
  if (xasl->proc.insert.obj_oid == NULL)
    {
      return NULL;
    }

  regu_alloc (regu);
  if (regu == NULL)
    {
      return regu;
    }
  regu->domain = pt_xasl_node_to_domain (parser, statement);

  /* set as linked to regu var */
  XASL_SET_FLAG (xasl, XASL_LINK_TO_REGU_VARIABLE);
  regu->xasl = xasl;
  regu->type = TYPE_CONSTANT;
  regu->value.dbvalptr = xasl->proc.insert.obj_oid;

  return regu;
}

/*
 * pt_set_numbering_node_etc_pre () -
 *   return:
 *   parser(in):
 *   node(in/out):
 *   arg(in/out):
 *   continue_walk(in):
 */
static PT_NODE *
pt_set_numbering_node_etc_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  SET_NUMBERING_NODE_ETC_INFO *info = (SET_NUMBERING_NODE_ETC_INFO *) arg;

  if (node->node_type == PT_EXPR)
    {
      if (info->instnum_valp && (node->info.expr.op == PT_INST_NUM || node->info.expr.op == PT_ROWNUM))
    {
      if (*info->instnum_valp == NULL)
        {
          regu_alloc (*info->instnum_valp);
        }

      node->etc = *info->instnum_valp;
    }

      if (info->ordbynum_valp && node->info.expr.op == PT_ORDERBY_NUM)
    {
      if (*info->ordbynum_valp == NULL)
        {
          regu_alloc (*info->ordbynum_valp);
        }

      node->etc = *info->ordbynum_valp;
    }
    }
  else if (node->node_type != PT_FUNCTION && node->node_type != PT_SORT_SPEC)
    {
      /* don't continue if it's not an expression, function or sort spec (analytic window's ORDER BY ROWNUM and
       * PARTITION BY ROWNUM) */
      *continue_walk = PT_STOP_WALK;
    }

  return node;
}

/*
 * pt_get_numbering_node_etc () - get the DB_VALUE reference of the
 *                ORDERBY_NUM expression
 * return : node
 * parser (in) : parser context
 * node (in)   : node
 * arg (in)    : pointer to DB_VALUE *
 * continue_walk (in) :
 */
PT_NODE *
pt_get_numbering_node_etc (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  if (node == NULL)
    {
      return node;
    }

  if (PT_IS_EXPR_NODE (node) && node->info.expr.op == PT_ORDERBY_NUM)
    {
      DB_VALUE **val_ptr = (DB_VALUE **) arg;
      *continue_walk = PT_STOP_WALK;
      *val_ptr = (DB_VALUE *) node->etc;
    }

  return node;
}

/*
 * pt_set_numbering_node_etc () - set etc values of parse tree nodes INST_NUM
 *                                and ORDERBY_NUM to pointers of corresponding
 *                                reguvars from XASL node
 *   return:
 *   parser(in):
 *   node_list(in):
 *   instnum_valp(out):
 *   ordbynum_valp(out):
 */
void
pt_set_numbering_node_etc (PARSER_CONTEXT * parser, PT_NODE * node_list, DB_VALUE ** instnum_valp,
               DB_VALUE ** ordbynum_valp)
{
  PT_NODE *node, *save_node, *save_next;
  SET_NUMBERING_NODE_ETC_INFO info;

  if (node_list)
    {
      info.instnum_valp = instnum_valp;
      info.ordbynum_valp = ordbynum_valp;

      for (node = node_list; node; node = node->next)
    {
      save_node = node;

      CAST_POINTER_TO_NODE (node);

      if (node)
        {
          /* save and cut-off node link */
          save_next = node->next;
          node->next = NULL;

          (void) parser_walk_tree (parser, node, pt_set_numbering_node_etc_pre, &info, pt_continue_walk, NULL);

          node->next = save_next;
        }

      node = save_node;
    }
    }
}


/*
 * pt_make_regu_numbering () - make a regu_variable constant for
 *                             inst_num() and orderby_num()
 *   return:
 *   parser(in):
 *   node(in):
 */
static REGU_VARIABLE *
pt_make_regu_numbering (PARSER_CONTEXT * parser, const PT_NODE * node)
{
  REGU_VARIABLE *regu = NULL;
  DB_VALUE *dbval;

  /* 'etc' field of PT_NODEs which belong to inst_num() or orderby_num() expression was set to points to
   * XASL_INSTNUM_VAL() or XASL_ORDBYNUM_VAL() by pt_set_numbering_node_etc() */
  dbval = (DB_VALUE *) node->etc;

  if (dbval)
    {
      regu_alloc (regu);
      if (regu)
    {
      regu->type = TYPE_CONSTANT;
      regu->domain = pt_xasl_node_to_domain (parser, node);
      regu->value.dbvalptr = dbval;
    }
    }
  else
    {
      if (parser && !pt_has_error (parser))
    {
      switch (node->info.expr.op)
        {
        case PT_INST_NUM:
        case PT_ROWNUM:
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INSTORDERBY_NUM_NOT_ALLOWED,
              "INST_NUM() or ROWNUM");
          break;

        case PT_ORDERBY_NUM:
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INSTORDERBY_NUM_NOT_ALLOWED,
              "ORDERBY_NUM()");
          break;

        default:
          assert (false);

        }
    }
    }

  return regu;
}


/*
 * pt_to_misc_operand () - maps PT_MISC_TYPE of PT_LEADING, PT_TRAILING,
 *      PT_BOTH, PT_YEAR, PT_MONTH, PT_DAY, PT_HOUR, PT_MINUTE, and PT_SECOND
 *      to the corresponding MISC_OPERAND
 *   return:
 *   regu(in/out):
 *   misc_specifier(in):
 */
static void
pt_to_misc_operand (REGU_VARIABLE * regu, PT_MISC_TYPE misc_specifier)
{
  if (regu && regu->value.arithptr)
    {
      regu->value.arithptr->misc_operand = pt_misc_to_qp_misc_operand (misc_specifier);
    }
}

/*
 * pt_make_prim_data_type_fortonum () -
 *   return:
 *   parser(in):
 *   prec(in):
 *   scale(in):
 */
PT_NODE *
pt_make_prim_data_type_fortonum (PARSER_CONTEXT * parser, int prec, int scale)
{
  PT_NODE *dt = NULL;

  dt = parser_new_node (parser, PT_DATA_TYPE);
  if (dt == NULL)
    {
      return NULL;
    }

  if (prec > DB_MAX_NUMERIC_PRECISION || scale > DB_MAX_NUMERIC_PRECISION || prec < 0 || scale < 0)
    {
      parser_free_tree (parser, dt);
      dt = NULL;
      return NULL;
    }

  dt->type_enum = PT_TYPE_NUMERIC;
  dt->info.data_type.precision = prec;
  dt->info.data_type.dec_precision = scale;

  return dt;
}

/*
 * pt_make_prim_data_type () -
 *   return:
 *   parser(in):
 *   e(in):
 */
PT_NODE *
pt_make_prim_data_type (PARSER_CONTEXT * parser, PT_TYPE_ENUM e)
{
  PT_NODE *dt = NULL;

  dt = parser_new_node (parser, PT_DATA_TYPE);

  if (dt == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

  dt->type_enum = e;
  dt->info.data_type.collation_flag = TP_DOMAIN_COLL_NORMAL;

  if (PT_HAS_COLLATION (e))
    {
      dt->info.data_type.units = (int) LANG_COERCIBLE_CODESET;
      dt->info.data_type.collation_id = LANG_COERCIBLE_COLL;
    }

  switch (e)
    {
    case PT_TYPE_INTEGER:
    case PT_TYPE_BIGINT:
    case PT_TYPE_SMALLINT:
    case PT_TYPE_DOUBLE:
    case PT_TYPE_DATE:
    case PT_TYPE_TIME:
    case PT_TYPE_TIMESTAMP:
    case PT_TYPE_TIMESTAMPTZ:
    case PT_TYPE_TIMESTAMPLTZ:
    case PT_TYPE_DATETIME:
    case PT_TYPE_DATETIMETZ:
    case PT_TYPE_DATETIMELTZ:
    case PT_TYPE_MONETARY:
    case PT_TYPE_BLOB:
    case PT_TYPE_CLOB:
    case PT_TYPE_JSON:
      dt->data_type = NULL;
      break;

    case PT_TYPE_CHAR:
      dt->info.data_type.precision = DB_MAX_CHAR_PRECISION;
      break;

    case PT_TYPE_VARCHAR:
      dt->info.data_type.precision = DB_MAX_VARCHAR_PRECISION;
      break;

    case PT_TYPE_BIT:
      dt->info.data_type.precision = DB_MAX_BIT_PRECISION;
      dt->info.data_type.units = INTL_CODESET_RAW_BITS;
      break;

    case PT_TYPE_VARBIT:
      dt->info.data_type.precision = DB_MAX_VARBIT_PRECISION;
      dt->info.data_type.units = INTL_CODESET_RAW_BITS;
      break;

    case PT_TYPE_NUMERIC:
      dt->info.data_type.precision = DB_MAX_NUMERIC_PRECISION;
      dt->info.data_type.dec_precision = DB_DEFAULT_NUMERIC_SCALE;
      break;

    default:
      /* error handling is required.. */
      parser_free_tree (parser, dt);
      dt = NULL;
    }

  return dt;
}

/*
 * pt_to_regu_resolve_domain () -
 *   return:
 *   p_precision(out):
 *   p_scale(out):
 *   node(in):
 */
void
pt_to_regu_resolve_domain (int *p_precision, int *p_scale, const PT_NODE * node)
{
  const char *format_buf;
  const char *fbuf_end_ptr;
  int format_sz;
  int precision, scale, maybe_sci_notation = 0;

  if (node == NULL)
    {
      *p_precision = DB_MAX_NUMERIC_PRECISION;
      *p_scale = DB_DEFAULT_NUMERIC_SCALE;
    }
  else
    {
      switch (node->info.value.db_value.data.ch.info.style)
    {
    case SMALL_STRING:
      format_sz = node->info.value.db_value.data.ch.sm.size;
      format_buf = (char *) node->info.value.db_value.data.ch.sm.buf;
      break;

    case MEDIUM_STRING:
      format_sz = node->info.value.db_value.data.ch.medium.size;
      format_buf = node->info.value.db_value.data.ch.medium.buf;
      break;

    default:
      format_sz = 0;
      format_buf = NULL;
    }

      fbuf_end_ptr = format_buf + format_sz - 1;

      precision = scale = 0;

      /* analyze format string */
      if (format_sz > 0)
    {
      /* skip white space or CR prefix */
      while (format_buf < fbuf_end_ptr && (*format_buf == ' ' || *format_buf == '\t' || *format_buf == '\n'))
        {
          format_buf++;
        }

      while (*format_buf != '.' && format_buf <= fbuf_end_ptr)
        {
          switch (*format_buf)
        {
        case '9':
        case '0':
          precision++;
          break;
        case '+':
        case '-':
        case ',':
        case ' ':
        case '\t':
        case '\n':
          break;

        case 'c':
        case 'C':
        case 's':
        case 'S':
          if (precision == 0)
            {
              break;
            }
          [[fallthrough]];

        default:
          maybe_sci_notation = 1;
        }
          format_buf++;
        }

      if (*format_buf == '.')
        {
          format_buf++;
          while (format_buf <= fbuf_end_ptr)
        {
          switch (*format_buf)
            {
            case '9':
            case '0':
              scale++;
            case '+':
            case '-':
            case ',':
            case ' ':
            case '\t':
            case '\n':
              break;

            default:
              maybe_sci_notation = 1;
            }
          format_buf++;
        }
        }

      precision += scale;
    }

      if (!maybe_sci_notation && (precision + scale) < DB_MAX_NUMERIC_PRECISION)
    {
      *p_precision = precision;
      *p_scale = scale;
    }
      else
    {
      *p_precision = DB_MAX_NUMERIC_PRECISION;
      *p_scale = DB_DEFAULT_NUMERIC_PRECISION;
    }
    }
}

/*
 * pt_make_prefix_index_data_filter  () - make data filter for index
 *                    with prefix
 *   return: the resulting data filter for index with prefix
 *   where_key_part(in): the key filter
 *   where_part(in): the data filter
 *   index_pred (in): the range
 */
static PT_NODE *
pt_make_prefix_index_data_filter (PARSER_CONTEXT * parser, PT_NODE * where_key_part, PT_NODE * where_part,
                  QO_XASL_INDEX_INFO * index_pred)
{
  PT_NODE *ipl_where_part = NULL;
  PT_NODE *diff_part;
  PT_NODE *ipl_if_part, *ipl_instnum_part;
  int i;
  PT_NODE *save_next = NULL;

  assert (parser != NULL);

  ipl_where_part = parser_copy_tree_list (parser, where_part);
  if ((index_pred == NULL || (index_pred && index_pred->nterms <= 0)) && where_key_part == NULL)
    {
      return ipl_where_part;
    }

  if (where_key_part)
    {
      diff_part = parser_get_tree_list_diff (parser, where_key_part, where_part);
      ipl_where_part = parser_append_node (diff_part, ipl_where_part);
    }

  if (index_pred && index_pred->nterms > 0)
    {
      PT_NODE *save_last = NULL;
      if (where_part)
    {
      save_last = where_part;
      while (save_last->next)
        {
          save_last = save_last->next;
        }
      save_last->next = where_key_part;
    }
      else
    {
      where_part = where_key_part;
    }

      for (i = 0; i < index_pred->nterms; i++)
    {
      save_next = index_pred->term_exprs[i]->next;
      index_pred->term_exprs[i]->next = NULL;
      diff_part = parser_get_tree_list_diff (parser, index_pred->term_exprs[i], where_part);
      pt_split_if_instnum (parser, diff_part, &ipl_if_part, &ipl_instnum_part);
      ipl_where_part = parser_append_node (ipl_if_part, ipl_where_part);
      parser_free_tree (parser, ipl_instnum_part);
      index_pred->term_exprs[i]->next = save_next;
    }

      if (save_last)
    {
      save_last->next = NULL;
    }
      else
    {
      where_part = NULL;
    }
    }

  return ipl_where_part;
}

/*
 * pt_to_regu_variable () - converts a parse expression tree to regu_variables
 *   return:
 *   parser(in):
 *   node(in): should be something that will evaluate to an expression
 *             of names and constant
 *   unbox(in):
 */
REGU_VARIABLE *
pt_to_regu_variable (PARSER_CONTEXT * parser, PT_NODE * node, UNBOX unbox)
{
  REGU_VARIABLE *regu = NULL;
  XASL_NODE *xasl;
  DB_VALUE *value, *val = NULL;
  TP_DOMAIN *domain;
  PT_NODE *data_type = NULL;
  PT_NODE *save_node = NULL, *save_next = NULL;
  REGU_VARIABLE *r1 = NULL, *r2 = NULL, *r3 = NULL;

  if (node == NULL)
    {
      regu_alloc (val);
      if (db_value_domain_init (val, DB_TYPE_VARCHAR, DB_DEFAULT_PRECISION, DB_DEFAULT_SCALE) == NO_ERROR)
    {
      regu = pt_make_regu_constant (parser, val, DB_TYPE_VARCHAR, NULL);
    }
    }
  else if (PT_IS_POINTER_REF_NODE (node))
    {
      PT_NODE *real_node = node->info.pointer.node;

      CAST_POINTER_TO_NODE (real_node);

      /* fetch domain from real node data type */
      domain = NULL;
      if (real_node != NULL && real_node->data_type != NULL)
    {
      domain = pt_node_data_type_to_db_domain (parser, real_node->data_type, real_node->type_enum);

      if (domain != NULL)
        {
          /* cache domain */
          domain = tp_domain_cache (domain);
        }
    }

      /* resolve to value domain if no domain was present */
      if (domain == NULL)
    {
      domain = tp_domain_resolve_value ((DB_VALUE *) node->etc, NULL);
    }

      /* set up regu var */
      regu_alloc (regu);
      regu->type = TYPE_CONSTANT;
      regu->domain = domain;
      regu->value.dbvalptr = (DB_VALUE *) node->etc;
    }
  else
    {
      save_node = node;

      CAST_POINTER_TO_NODE (node);

      if (node != NULL && node->type_enum == PT_TYPE_LOGICAL
      && (node->node_type == PT_EXPR || node->node_type == PT_VALUE))
    {
      regu = pt_make_regu_pred (pt_to_pred_expr (parser, node));
    }
      else if (node != NULL)
    {
      /* save and cut-off node link */
      save_next = node->next;
      node->next = NULL;

      switch (node->node_type)
        {
        case PT_DOT_:
          /* a path expression. XASL fetch procs or equivalent should already be done for it return the regu
           * variable for the right most name in the path expression. */
          switch (node->info.dot.arg2->info.name.meta_class)
        {
        case PT_PARAMETER:
          regu_alloc (val);
          pt_evaluate_tree (parser, node, val, 1);
          if (!pt_has_error (parser))
            {
              regu = pt_make_regu_constant (parser, val, pt_node_to_db_type (node), node);
            }
          break;
        case PT_META_ATTR:
        case PT_NORMAL:
        case PT_SHARED:
        default:
          regu = pt_attribute_to_regu (parser, node->info.dot.arg2);
          break;
        }
          break;

        case PT_METHOD_CALL:
          /*
             TODO : JSP containing column cannot be here because the query is rewritten in meth_translate().
             The index scan may not work because of the query rewrite in meth_translate().
             The method call should proceed in the same way as the internal function.
             pt_to_regu_variable() : generate regu_var for jsp function
             fetch_peek_dbval() : fetch regu_var for jsp function
           */

          /* a method call that can be evaluated as a constant expression. */
          if (PT_IS_METHOD (node))
        {
          /* a method call that can be evaluated as a constant expression. */
          regu_alloc (val);
          pt_evaluate_tree (parser, node, val, 1);
          if (!pt_has_error (parser))
            {
              regu = pt_make_regu_constant (parser, val, pt_node_to_db_type (node), node);
            }
        }
          else
        {
          regu = pt_stored_procedure_to_regu (parser, node);
          if (regu == NULL)
            {
              PT_ERRORc (parser, node, er_msg ());
            }
        }
          break;

        case PT_EXPR:
          if (node->info.expr.op == PT_FUNCTION_HOLDER)
        {
          //TODO FIND WHY NEXT WASN'T RESTORED
          node->next = save_next;
          regu = pt_function_to_regu (parser, node->info.expr.arg1);
          return regu;
        }

          if (PT_REQUIRES_HIERARCHICAL_QUERY (node->info.expr.op))
        {
          if (parser->symbols && parser->symbols->query_node)
            {
              if ((parser->symbols->query_node->node_type != PT_SELECT)
              || (parser->symbols->query_node->info.query.q.select.connect_by == NULL))
            {
              const char *opcode = pt_show_binopcode (node->info.expr.op);
              char *temp_buffer = (char *) malloc (strlen (opcode) + 1);
              if (temp_buffer)
                {
                  strcpy (temp_buffer, opcode);
                  ustr_upper (temp_buffer);
                }
              PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_HIERACHICAL_QUERY,
                      temp_buffer ? temp_buffer : opcode);
              if (temp_buffer)
                {
                  free (temp_buffer);
                }
            }
              if (node->info.expr.op == PT_CONNECT_BY_ISCYCLE
              && ((parser->symbols->query_node->node_type != PT_SELECT)
                  || (parser->symbols->query_node->info.query.q.select.check_cycles !=
                  CONNECT_BY_CYCLES_NONE
                  && parser->symbols->query_node->info.query.q.select.check_cycles !=
                  CONNECT_BY_CYCLES_NONE_IGNORE)))
            {
              PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC,
                     MSGCAT_SEMANTIC_ISCYCLE_REQUIRES_NOCYCLE);
            }
            }
          else
            {
              assert (false);
            }
        }
          domain = NULL;
          if (node->info.expr.op == PT_PLUS || node->info.expr.op == PT_MINUS || node->info.expr.op == PT_TIMES
          || node->info.expr.op == PT_DIVIDE || node->info.expr.op == PT_MODULUS
          || node->info.expr.op == PT_POWER || node->info.expr.op == PT_AES_ENCRYPT
          || node->info.expr.op == PT_AES_DECRYPT || node->info.expr.op == PT_SHA_TWO
          || node->info.expr.op == PT_ROUND || node->info.expr.op == PT_LOG || node->info.expr.op == PT_TRUNC
          || node->info.expr.op == PT_POSITION || node->info.expr.op == PT_FINDINSET
          || node->info.expr.op == PT_LPAD || node->info.expr.op == PT_RPAD || node->info.expr.op == PT_REPLACE
          || node->info.expr.op == PT_TRANSLATE || node->info.expr.op == PT_ADD_MONTHS
          || node->info.expr.op == PT_MONTHS_BETWEEN || node->info.expr.op == PT_FORMAT
          || node->info.expr.op == PT_ATAN || node->info.expr.op == PT_ATAN2
          || node->info.expr.op == PT_DATE_FORMAT || node->info.expr.op == PT_STR_TO_DATE
          || node->info.expr.op == PT_TIME_FORMAT || node->info.expr.op == PT_DATEDIFF
          || node->info.expr.op == PT_TIMEDIFF || node->info.expr.op == PT_TO_NUMBER
          || node->info.expr.op == PT_LEAST || node->info.expr.op == PT_GREATEST
          || node->info.expr.op == PT_CASE || node->info.expr.op == PT_NULLIF
          || node->info.expr.op == PT_COALESCE || node->info.expr.op == PT_NVL
          || node->info.expr.op == PT_DECODE || node->info.expr.op == PT_STRCAT
          || node->info.expr.op == PT_SYS_CONNECT_BY_PATH || node->info.expr.op == PT_BIT_AND
          || node->info.expr.op == PT_BIT_OR || node->info.expr.op == PT_BIT_XOR
          || node->info.expr.op == PT_BITSHIFT_LEFT || node->info.expr.op == PT_BITSHIFT_RIGHT
          || node->info.expr.op == PT_DIV || node->info.expr.op == PT_MOD || node->info.expr.op == PT_IFNULL
          || node->info.expr.op == PT_CONCAT || node->info.expr.op == PT_LEFT || node->info.expr.op == PT_RIGHT
          || node->info.expr.op == PT_STRCMP || node->info.expr.op == PT_REPEAT
          || node->info.expr.op == PT_WEEKF || node->info.expr.op == PT_MAKEDATE
          || node->info.expr.op == PT_ADDTIME || node->info.expr.op == PT_DEFINE_VARIABLE
          || node->info.expr.op == PT_CHR || node->info.expr.op == PT_CLOB_TO_CHAR
          || node->info.expr.op == PT_INDEX_PREFIX || node->info.expr.op == PT_FROM_TZ)
        {
          r1 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
          if ((node->info.expr.op == PT_CONCAT) && node->info.expr.arg2 == NULL)
            {
              r2 = NULL;
            }
          else
            {
              r2 = pt_to_regu_variable (parser, node->info.expr.arg2, unbox);
            }
          if (node->info.expr.op != PT_ADD_MONTHS && node->info.expr.op != PT_MONTHS_BETWEEN
              && node->info.expr.op != PT_TO_NUMBER)
            {
              if (node->type_enum == PT_TYPE_MAYBE)
            {
              if (pt_is_op_hv_late_bind (node->info.expr.op))
                {
                  domain = pt_xasl_node_to_domain (parser, node);
                }
              else
                {
                  domain = node->expected_domain;
                }
            }
              else
            {
              domain = pt_xasl_node_to_domain (parser, node);
            }
              if (domain == NULL)
            {
              goto end_expr_op_switch;
            }
            }

          if (node->info.expr.op == PT_SYS_CONNECT_BY_PATH)
            {
              regu_alloc (r3);
              r3->domain = pt_xasl_node_to_domain (parser, node);
              r3->xasl = (XASL_NODE *) node->etc;
              r3->type = TYPE_CONSTANT;
              r3->value.dbvalptr = NULL;
            }

          if (node->info.expr.op == PT_ATAN && node->info.expr.arg2 == NULL)
            {
              /* If ATAN has only one arg, treat it as an unary op */
              r2 = r1;
              r1 = NULL;
            }

          if (node->info.expr.op == PT_DATE_FORMAT || node->info.expr.op == PT_STR_TO_DATE
              || node->info.expr.op == PT_TIME_FORMAT || node->info.expr.op == PT_FORMAT
              || node->info.expr.op == PT_INDEX_PREFIX)
            {
              r3 = pt_to_regu_variable (parser, node->info.expr.arg3, unbox);
            }
        }
          else if (node->info.expr.op == PT_DEFAULTF)
        {
          assert (false);
          regu = NULL;
        }
          else if (node->info.expr.op == PT_UNIX_TIMESTAMP)
        {
          r1 = NULL;
          if (!node->info.expr.arg1)
            {
              r2 = NULL;
            }
          else
            {
              r2 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
            }
          if (node->type_enum == PT_TYPE_MAYBE)
            {
              assert (false);
              domain = node->expected_domain;
            }
          else
            {
              domain = pt_xasl_node_to_domain (parser, node);
            }
          if (domain == NULL)
            {
              goto end_expr_op_switch;
            }
        }
          else if (node->info.expr.op == PT_UNARY_MINUS || node->info.expr.op == PT_RAND
               || node->info.expr.op == PT_DRAND || node->info.expr.op == PT_RANDOM
               || node->info.expr.op == PT_DRANDOM || node->info.expr.op == PT_FLOOR
               || node->info.expr.op == PT_CEIL || node->info.expr.op == PT_SIGN || node->info.expr.op == PT_EXP
               || node->info.expr.op == PT_SQRT || node->info.expr.op == PT_ACOS
               || node->info.expr.op == PT_ASIN || node->info.expr.op == PT_COS || node->info.expr.op == PT_SIN
               || node->info.expr.op == PT_TAN || node->info.expr.op == PT_COT
               || node->info.expr.op == PT_DEGREES || node->info.expr.op == PT_DATEF
               || node->info.expr.op == PT_TIMEF || node->info.expr.op == PT_RADIANS
               || node->info.expr.op == PT_LN || node->info.expr.op == PT_LOG2 || node->info.expr.op == PT_LOG10
               || node->info.expr.op == PT_ABS || node->info.expr.op == PT_OCTET_LENGTH
               || node->info.expr.op == PT_BIT_LENGTH || node->info.expr.op == PT_CHAR_LENGTH
               || node->info.expr.op == PT_LOWER || node->info.expr.op == PT_UPPER
               || node->info.expr.op == PT_HEX || node->info.expr.op == PT_ASCII
               || node->info.expr.op == PT_LAST_DAY || node->info.expr.op == PT_CAST
               || node->info.expr.op == PT_EXTRACT || node->info.expr.op == PT_ENCRYPT
               || node->info.expr.op == PT_DECRYPT || node->info.expr.op == PT_BIN
               || node->info.expr.op == PT_MD5 || node->info.expr.op == PT_SHA_ONE
               || node->info.expr.op == PT_SPACE || node->info.expr.op == PT_PRIOR
               || node->info.expr.op == PT_CONNECT_BY_ROOT || node->info.expr.op == PT_QPRIOR
               || node->info.expr.op == PT_BIT_NOT || node->info.expr.op == PT_REVERSE
               || node->info.expr.op == PT_BIT_COUNT || node->info.expr.op == PT_ISNULL
               || node->info.expr.op == PT_TYPEOF || node->info.expr.op == PT_YEARF
               || node->info.expr.op == PT_MONTHF || node->info.expr.op == PT_DAYF
               || node->info.expr.op == PT_DAYOFMONTH || node->info.expr.op == PT_HOURF
               || node->info.expr.op == PT_MINUTEF || node->info.expr.op == PT_SECONDF
               || node->info.expr.op == PT_QUARTERF || node->info.expr.op == PT_WEEKDAY
               || node->info.expr.op == PT_DAYOFWEEK || node->info.expr.op == PT_DAYOFYEAR
               || node->info.expr.op == PT_TODAYS || node->info.expr.op == PT_FROMDAYS
               || node->info.expr.op == PT_TIMETOSEC || node->info.expr.op == PT_SECTOTIME
               || node->info.expr.op == PT_EVALUATE_VARIABLE || node->info.expr.op == PT_TO_ENUMERATION_VALUE
               || node->info.expr.op == PT_INET_ATON || node->info.expr.op == PT_INET_NTOA
               || node->info.expr.op == PT_CHARSET || node->info.expr.op == PT_COLLATION
               || node->info.expr.op == PT_TO_BASE64 || node->info.expr.op == PT_FROM_BASE64
               || node->info.expr.op == PT_FROM_BASE64 || node->info.expr.op == PT_SLEEP
               || node->info.expr.op == PT_TZ_OFFSET || node->info.expr.op == PT_CRC32
               || node->info.expr.op == PT_DISK_SIZE || node->info.expr.op == PT_CONV_TZ)
        {
          r1 = NULL;

          if (node->info.expr.op == PT_PRIOR)
            {
              PT_NODE *saved_current_class;

              /* we want TYPE_CONSTANT regu vars in PRIOR arg expr */
              saved_current_class = parser->symbols->current_class;
              parser->symbols->current_class = NULL;

              r2 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);

              parser->symbols->current_class = saved_current_class;
            }
          else
            {
              r2 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
            }

          if (node->info.expr.op == PT_CONNECT_BY_ROOT || node->info.expr.op == PT_QPRIOR)
            {
              regu_alloc (r3);
              r3->domain = pt_xasl_node_to_domain (parser, node);
              r3->xasl = (XASL_NODE *) node->etc;
              r3->type = TYPE_CONSTANT;
              r3->value.dbvalptr = NULL;
            }

          if (node->info.expr.op != PT_LAST_DAY && node->info.expr.op != PT_CAST)
            {
              if (node->type_enum == PT_TYPE_MAYBE)
            {
              if (pt_is_op_hv_late_bind (node->info.expr.op))
                {
                  domain = pt_xasl_node_to_domain (parser, node);
                }
              else
                {
                  domain = node->expected_domain;
                }
            }
              else
            {
              domain = pt_xasl_node_to_domain (parser, node);
            }
              if (domain == NULL)
            {
              goto end_expr_op_switch;
            }
            }
        }
          else if (node->info.expr.op == PT_TIMESTAMP || node->info.expr.op == PT_LIKE_LOWER_BOUND
               || node->info.expr.op == PT_LIKE_UPPER_BOUND)
        {
          r1 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
          if (!node->info.expr.arg2)
            {
              r2 = NULL;
            }
          else
            {
              r2 = pt_to_regu_variable (parser, node->info.expr.arg2, unbox);
            }

          domain = pt_xasl_node_to_domain (parser, node);
          if (domain == NULL)
            {
              goto end_expr_op_switch;
            }
        }
          else if (node->info.expr.op == PT_DATE_ADD || node->info.expr.op == PT_DATE_SUB)
        {
          DB_VALUE *val;

          r1 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
          r2 = pt_to_regu_variable (parser, node->info.expr.arg2, unbox);
          /* store the info.expr.qualifier which is the unit parameter into a constant regu variable */
          regu_alloc (val);
          if (val)
            {
              db_make_int (val, node->info.expr.arg3->info.expr.qualifier);
              r3 = pt_make_regu_constant (parser, val, DB_TYPE_INTEGER, NULL);
            }

          if (node->type_enum == PT_TYPE_MAYBE)
            {
              domain = node->expected_domain;
            }
          else
            {
              domain = pt_xasl_node_to_domain (parser, node);
            }
          if (domain == NULL)
            {
              goto end_expr_op_switch;
            }
        }
          else if (node->info.expr.op == PT_ADDDATE || node->info.expr.op == PT_SUBDATE)
        {
          r1 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
          r2 = pt_to_regu_variable (parser, node->info.expr.arg2, unbox);

          if (node->type_enum == PT_TYPE_MAYBE)
            {
              domain = node->expected_domain;
            }
          else
            {
              domain = pt_xasl_node_to_domain (parser, node);
            }
          if (domain == NULL)
            {
              goto end_expr_op_switch;
            }
        }
          else if (node->info.expr.op == PT_INCR || node->info.expr.op == PT_DECR || node->info.expr.op == PT_INSTR
               || node->info.expr.op == PT_SUBSTRING || node->info.expr.op == PT_NVL2
               || node->info.expr.op == PT_CONCAT_WS || node->info.expr.op == PT_FIELD
               || node->info.expr.op == PT_LOCATE || node->info.expr.op == PT_MID
               || node->info.expr.op == PT_SUBSTRING_INDEX || node->info.expr.op == PT_MAKETIME
               || node->info.expr.op == PT_INDEX_CARDINALITY || node->info.expr.op == PT_NEW_TIME)
        {
          r1 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
          if (node->info.expr.arg2 == NULL && node->info.expr.op == PT_CONCAT_WS)
            {
              r2 = NULL;
            }
          else
            {
              r2 = pt_to_regu_variable (parser, node->info.expr.arg2, unbox);
            }

          if (node->info.expr.arg3 == NULL
              && (node->info.expr.op == PT_LOCATE || node->info.expr.op == PT_SUBSTRING))
            {
              r3 = NULL;
            }
          else
            {
              r3 = pt_to_regu_variable (parser, node->info.expr.arg3, unbox);
            }
          if (node->type_enum == PT_TYPE_MAYBE)
            {
              if (pt_is_op_hv_late_bind (node->info.expr.op))
            {
              domain = pt_xasl_node_to_domain (parser, node);
            }
              else
            {
              domain = node->expected_domain;
            }
            }
          else
            {
              domain = pt_xasl_node_to_domain (parser, node);
            }
          if (domain == NULL)
            {
              goto end_expr_op_switch;
            }
        }
          else if (node->info.expr.op == PT_CONV)
        {
          r1 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
          r2 = pt_to_regu_variable (parser, node->info.expr.arg2, unbox);
          r3 = pt_to_regu_variable (parser, node->info.expr.arg3, unbox);
          domain = pt_xasl_node_to_domain (parser, node);
          if (domain == NULL)
            {
              goto end_expr_op_switch;
            }
        }
          else if (node->info.expr.op == PT_TO_CHAR || node->info.expr.op == PT_TO_DATE
               || node->info.expr.op == PT_TO_TIME || node->info.expr.op == PT_TO_TIMESTAMP
               || node->info.expr.op == PT_TO_DATETIME || node->info.expr.op == PT_TO_DATETIME_TZ
               || node->info.expr.op == PT_TO_TIMESTAMP_TZ)
        {
          r1 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
          r2 = pt_to_regu_variable (parser, node->info.expr.arg2, unbox);
          r3 = pt_to_regu_variable (parser, node->info.expr.arg3, unbox);
        }
          else if (node->info.expr.op == PT_SYS_DATE || node->info.expr.op == PT_CURRENT_DATE
               || node->info.expr.op == PT_SYS_TIME || node->info.expr.op == PT_CURRENT_TIME
               || node->info.expr.op == PT_SYS_TIMESTAMP || node->info.expr.op == PT_CURRENT_TIMESTAMP
               || node->info.expr.op == PT_SYS_DATETIME || node->info.expr.op == PT_CURRENT_DATETIME
               || node->info.expr.op == PT_UTC_TIME || node->info.expr.op == PT_UTC_DATE
               || node->info.expr.op == PT_PI || node->info.expr.op == PT_LOCAL_TRANSACTION_ID
               || node->info.expr.op == PT_ROW_COUNT || node->info.expr.op == PT_LIST_DBS
               || node->info.expr.op == PT_SYS_GUID || node->info.expr.op == PT_LAST_INSERT_ID
               || node->info.expr.op == PT_DBTIMEZONE || node->info.expr.op == PT_SESSIONTIMEZONE
               || node->info.expr.op == PT_UTC_TIMESTAMP)
        {
          domain = pt_xasl_node_to_domain (parser, node);
          if (domain == NULL)
            {
              goto end_expr_op_switch;
            }
        }
          else if (node->info.expr.op == PT_BIT_TO_BLOB || node->info.expr.op == PT_CHAR_TO_BLOB
               || node->info.expr.op == PT_BLOB_LENGTH || node->info.expr.op == PT_CHAR_TO_CLOB
               || node->info.expr.op == PT_CLOB_LENGTH)
        {
          r1 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
          r2 = NULL;
          r3 = NULL;
        }
          else if (node->info.expr.op == PT_BLOB_TO_BIT)
        {
          r1 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
          if (node->info.expr.arg2 == NULL)
            {
              r2 = NULL;
            }
          else
            {
              r2 = pt_to_regu_variable (parser, node->info.expr.arg2, unbox);
            }
          r3 = NULL;
        }
          else if (node->info.expr.op == PT_IF)
        {
          r1 = pt_to_regu_variable (parser, node->info.expr.arg2, unbox);
          r2 = pt_to_regu_variable (parser, node->info.expr.arg3, unbox);

          if (node->type_enum == PT_TYPE_MAYBE)
            {
              domain = node->expected_domain;
            }
          else
            {
              domain = pt_xasl_node_to_domain (parser, node);
            }
          if (domain == NULL)
            {
              goto end_expr_op_switch;
            }
        }
          else if (node->info.expr.op == PT_EXEC_STATS)
        {
          r1 = NULL;
          r2 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
          r3 = NULL;

          domain = pt_xasl_node_to_domain (parser, node);
          if (domain == NULL)
            {
              goto end_expr_op_switch;
            }
        }
          else if (node->info.expr.op == PT_WIDTH_BUCKET)
        {
          r1 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
          r2 = pt_to_regu_variable (parser, node->info.expr.arg2, unbox);
          r3 = pt_to_regu_variable (parser, node->info.expr.arg3, unbox);

          domain = pt_xasl_node_to_domain (parser, node);
          if (domain == NULL)
            {
              goto end_expr_op_switch;
            }
        }
          else if (node->info.expr.op == PT_TRACE_STATS)
        {
          r1 = NULL;
          r2 = NULL;
          r3 = NULL;

          domain = pt_xasl_node_to_domain (parser, node);
          if (domain == NULL)
            {
              goto end_expr_op_switch;
            }
        }

          switch (node->info.expr.op)
        {
        case PT_PLUS:
          regu = pt_make_regu_arith (r1, r2, NULL, T_ADD, domain);
          break;

        case PT_MINUS:
          regu = pt_make_regu_arith (r1, r2, NULL, T_SUB, domain);
          break;

        case PT_TIMES:
          regu = pt_make_regu_arith (r1, r2, NULL, T_MUL, domain);
          break;

        case PT_DIVIDE:
          regu = pt_make_regu_arith (r1, r2, NULL, T_DIV, domain);
          break;

        case PT_UNARY_MINUS:
          regu = pt_make_regu_arith (r1, r2, NULL, T_UNMINUS, domain);
          break;

        case PT_BIT_NOT:
          regu = pt_make_regu_arith (r1, r2, NULL, T_BIT_NOT, domain);
          break;

        case PT_BIT_AND:
          regu = pt_make_regu_arith (r1, r2, NULL, T_BIT_AND, domain);
          break;

        case PT_BIT_OR:
          regu = pt_make_regu_arith (r1, r2, NULL, T_BIT_OR, domain);
          break;

        case PT_BIT_XOR:
          regu = pt_make_regu_arith (r1, r2, NULL, T_BIT_XOR, domain);
          break;

        case PT_BITSHIFT_LEFT:
          regu = pt_make_regu_arith (r1, r2, NULL, T_BITSHIFT_LEFT, domain);
          break;

        case PT_BITSHIFT_RIGHT:
          regu = pt_make_regu_arith (r1, r2, NULL, T_BITSHIFT_RIGHT, domain);
          break;

        case PT_DIV:
          regu = pt_make_regu_arith (r1, r2, NULL, T_INTDIV, domain);
          break;

        case PT_MOD:
          regu = pt_make_regu_arith (r1, r2, NULL, T_INTMOD, domain);
          break;

        case PT_IF:
          regu = pt_make_regu_arith (r1, r2, NULL, T_IF, domain);
          if (regu == NULL)
            {
              break;
            }
          regu->value.arithptr->pred = pt_to_pred_expr (parser, node->info.expr.arg1);
          break;

        case PT_IFNULL:
          regu = pt_make_regu_arith (r1, r2, NULL, T_IFNULL, domain);
          break;

        case PT_CONCAT:
          regu = pt_make_regu_arith (r1, r2, NULL, T_CONCAT, domain);
          break;
        case PT_CONCAT_WS:
          regu = pt_make_regu_arith (r1, r2, r3, T_CONCAT_WS, domain);
          break;

        case PT_FIELD:
          REGU_VARIABLE_SET_FLAG (r1, REGU_VARIABLE_FIELD_NESTED);
          regu = pt_make_regu_arith (r1, r2, r3, T_FIELD, domain);

          if (node->info.expr.arg3 && node->info.expr.arg3->next
              && node->info.expr.arg3->next->info.value.data_value.i == 1)
            {
              /* bottom level T_FIELD */
              REGU_VARIABLE_SET_FLAG (regu, REGU_VARIABLE_FIELD_COMPARE);
            }
          break;

        case PT_LEFT:
          regu = pt_make_regu_arith (r1, r2, NULL, T_LEFT, domain);
          break;

        case PT_RIGHT:
          regu = pt_make_regu_arith (r1, r2, NULL, T_RIGHT, domain);
          break;

        case PT_REPEAT:
          regu = pt_make_regu_arith (r1, r2, NULL, T_REPEAT, domain);
          break;

        case PT_TIME_FORMAT:
          regu = pt_make_regu_arith (r1, r2, r3, T_TIME_FORMAT, domain);
          break;

        case PT_DATE_SUB:
          regu = pt_make_regu_arith (r1, r2, r3, T_DATE_SUB, domain);
          break;

        case PT_DATE_ADD:
          regu = pt_make_regu_arith (r1, r2, r3, T_DATE_ADD, domain);
          break;

        case PT_LOCATE:
          regu = pt_make_regu_arith (r1, r2, r3, T_LOCATE, domain);
          break;

        case PT_MID:
          regu = pt_make_regu_arith (r1, r2, r3, T_MID, domain);
          break;

        case PT_STRCMP:
          regu = pt_make_regu_arith (r1, r2, NULL, T_STRCMP, domain);
          break;

        case PT_REVERSE:
          regu = pt_make_regu_arith (r1, r2, NULL, T_REVERSE, domain);
          break;

        case PT_DISK_SIZE:
          regu = pt_make_regu_arith (r1, r2, NULL, T_DISK_SIZE, domain);
          break;

        case PT_BIT_COUNT:
          regu = pt_make_regu_arith (r1, r2, NULL, T_BIT_COUNT, domain);
          break;

        case PT_ISNULL:
          regu = pt_make_regu_arith (r1, r2, NULL, T_ISNULL, domain);
          break;

        case PT_EVALUATE_VARIABLE:
          regu = pt_make_regu_arith (r1, r2, NULL, T_EVALUATE_VARIABLE, domain);
          break;

        case PT_DEFINE_VARIABLE:
          regu = pt_make_regu_arith (r1, r2, NULL, T_DEFINE_VARIABLE, domain);
          break;

        case PT_YEARF:
          regu = pt_make_regu_arith (r1, r2, NULL, T_YEAR, domain);
          break;

        case PT_MONTHF:
          regu = pt_make_regu_arith (r1, r2, NULL, T_MONTH, domain);
          break;

        case PT_DAYOFMONTH:
        case PT_DAYF:
          regu = pt_make_regu_arith (r1, r2, NULL, T_DAY, domain);
          break;

        case PT_HOURF:
          regu = pt_make_regu_arith (r1, r2, NULL, T_HOUR, domain);
          break;

        case PT_MINUTEF:
          regu = pt_make_regu_arith (r1, r2, NULL, T_MINUTE, domain);
          break;

        case PT_SECONDF:
          regu = pt_make_regu_arith (r1, r2, NULL, T_SECOND, domain);
          break;

        case PT_UNIX_TIMESTAMP:
          regu = pt_make_regu_arith (NULL, r2, NULL, T_UNIX_TIMESTAMP, domain);
          break;

        case PT_TIMESTAMP:
          regu = pt_make_regu_arith (r1, r2, NULL, T_TIMESTAMP, domain);
          break;

        case PT_LIKE_LOWER_BOUND:
          regu = pt_make_regu_arith (r1, r2, NULL, T_LIKE_LOWER_BOUND, domain);
          break;

        case PT_LIKE_UPPER_BOUND:
          regu = pt_make_regu_arith (r1, r2, NULL, T_LIKE_UPPER_BOUND, domain);
          break;

        case PT_QUARTERF:
          regu = pt_make_regu_arith (r1, r2, NULL, T_QUARTER, domain);
          break;

        case PT_WEEKDAY:
          regu = pt_make_regu_arith (r1, r2, NULL, T_WEEKDAY, domain);
          break;

        case PT_DAYOFWEEK:
          regu = pt_make_regu_arith (r1, r2, NULL, T_DAYOFWEEK, domain);
          break;

        case PT_DAYOFYEAR:
          regu = pt_make_regu_arith (r1, r2, NULL, T_DAYOFYEAR, domain);
          break;

        case PT_TODAYS:
          regu = pt_make_regu_arith (r1, r2, NULL, T_TODAYS, domain);
          break;

        case PT_FROMDAYS:
          regu = pt_make_regu_arith (r1, r2, NULL, T_FROMDAYS, domain);
          break;

        case PT_TIMETOSEC:
          regu = pt_make_regu_arith (r1, r2, NULL, T_TIMETOSEC, domain);
          break;

        case PT_SECTOTIME:
          regu = pt_make_regu_arith (r1, r2, NULL, T_SECTOTIME, domain);
          break;

        case PT_MAKEDATE:
          regu = pt_make_regu_arith (r1, r2, NULL, T_MAKEDATE, domain);
          break;

        case PT_MAKETIME:
          regu = pt_make_regu_arith (r1, r2, r3, T_MAKETIME, domain);
          break;

        case PT_NEW_TIME:
          regu = pt_make_regu_arith (r1, r2, r3, T_NEW_TIME, domain);
          break;

        case PT_ADDTIME:
          regu = pt_make_regu_arith (r1, r2, NULL, T_ADDTIME, domain);
          break;

        case PT_FROM_TZ:
          regu = pt_make_regu_arith (r1, r2, NULL, T_FROM_TZ, domain);
          break;

        case PT_WEEKF:
          regu = pt_make_regu_arith (r1, r2, NULL, T_WEEK, domain);
          break;

        case PT_DATABASE:
          {
            PT_NODE *dbname_val;
            char *dbname;

            dbname_val = parser_new_node (parser, PT_VALUE);
            if (dbname_val == NULL)
              {
            PT_INTERNAL_ERROR (parser, "allocate new node");
            return NULL;
              }

            dbname = db_get_database_name ();
            if (dbname)
              {
            dbname_val->type_enum = PT_TYPE_VARCHAR;
            dbname_val->info.value.string_type = ' ';

            dbname_val->info.value.data_value.str = pt_append_nulstring (parser, NULL, dbname);
            PT_NODE_PRINT_VALUE_TO_TEXT (parser, dbname_val);

            db_string_free (dbname);

            /* copy data type (to apply collation and codeset) */
            assert (dbname_val->data_type == NULL);
            dbname_val->data_type = parser_copy_tree (parser, node->data_type);
            assert (dbname_val->data_type->type_enum == dbname_val->type_enum);
              }
            else
              {
            dbname_val->type_enum = PT_TYPE_NULL;
              }

            regu = pt_to_regu_variable (parser, dbname_val, unbox);
            break;
          }
        case PT_VERSION:
          {
            PT_NODE *dbversion_val;
            char *dbversion;

            dbversion_val = parser_new_node (parser, PT_VALUE);
            if (dbversion_val == NULL)
              {
            PT_INTERNAL_ERROR (parser, "allocate new node");
            return NULL;
              }

            dbversion = db_get_database_version ();
            if (dbversion)
              {
            dbversion_val->type_enum = node->type_enum;
            dbversion_val->info.value.string_type = ' ';

            dbversion_val->info.value.data_value.str = pt_append_nulstring (parser, NULL, dbversion);
            PT_NODE_PRINT_VALUE_TO_TEXT (parser, dbversion_val);

            db_string_free (dbversion);

            /* copy data type (to apply collation and codeset) */
            assert (dbversion_val->data_type == NULL);
            dbversion_val->data_type = parser_copy_tree (parser, node->data_type);
            assert (dbversion_val->data_type->type_enum == dbversion_val->type_enum);
              }
            else
              {
            dbversion_val->type_enum = PT_TYPE_NULL;
              }

            regu = pt_to_regu_variable (parser, dbversion_val, unbox);
            break;
          }

        case PT_PRIOR:
          regu = pt_make_regu_arith (r1, r2, NULL, T_PRIOR, domain);
          break;

        case PT_CONNECT_BY_ROOT:
          regu = pt_make_regu_arith (r1, r2, r3, T_CONNECT_BY_ROOT, domain);
          break;

        case PT_QPRIOR:
          regu = pt_make_regu_arith (r1, r2, r3, T_QPRIOR, domain);
          break;

        case PT_MODULUS:
          regu = pt_make_regu_arith (r1, r2, NULL, T_MOD, domain);
          parser->etc = (void *) 1;
          break;

        case PT_PI:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_PI, domain);
          break;

        case PT_RAND:
          regu = pt_make_regu_arith (NULL, r2, NULL, T_RAND, domain);
          break;

        case PT_DRAND:
          regu = pt_make_regu_arith (NULL, r2, NULL, T_DRAND, domain);
          break;

        case PT_RANDOM:
          regu = pt_make_regu_arith (NULL, r2, NULL, T_RANDOM, domain);
          break;

        case PT_DRANDOM:
          regu = pt_make_regu_arith (NULL, r2, NULL, T_DRANDOM, domain);
          break;

        case PT_FLOOR:
          regu = pt_make_regu_arith (r1, r2, NULL, T_FLOOR, domain);
          break;

        case PT_CEIL:
          regu = pt_make_regu_arith (r1, r2, NULL, T_CEIL, domain);
          break;

        case PT_SIGN:
          regu = pt_make_regu_arith (r1, r2, NULL, T_SIGN, domain);
          break;

        case PT_POWER:
          regu = pt_make_regu_arith (r1, r2, NULL, T_POWER, domain);
          break;

        case PT_ROUND:
          regu = pt_make_regu_arith (r1, r2, NULL, T_ROUND, domain);
          break;

        case PT_LOG:
          regu = pt_make_regu_arith (r1, r2, NULL, T_LOG, domain);
          break;

        case PT_EXP:
          regu = pt_make_regu_arith (r1, r2, NULL, T_EXP, domain);
          break;

        case PT_SQRT:
          regu = pt_make_regu_arith (r1, r2, NULL, T_SQRT, domain);
          break;

        case PT_COS:
          regu = pt_make_regu_arith (r1, r2, NULL, T_COS, domain);
          break;

        case PT_SIN:
          regu = pt_make_regu_arith (r1, r2, NULL, T_SIN, domain);
          break;

        case PT_TAN:
          regu = pt_make_regu_arith (r1, r2, NULL, T_TAN, domain);
          break;

        case PT_COT:
          regu = pt_make_regu_arith (r1, r2, NULL, T_COT, domain);
          break;

        case PT_ACOS:
          regu = pt_make_regu_arith (r1, r2, NULL, T_ACOS, domain);
          break;

        case PT_ASIN:
          regu = pt_make_regu_arith (r1, r2, NULL, T_ASIN, domain);
          break;

        case PT_ATAN:
          regu = pt_make_regu_arith (r1, r2, NULL, T_ATAN, domain);
          break;

        case PT_ATAN2:
          regu = pt_make_regu_arith (r1, r2, NULL, T_ATAN2, domain);
          break;

        case PT_DEGREES:
          regu = pt_make_regu_arith (r1, r2, NULL, T_DEGREES, domain);
          break;

        case PT_DATEF:
          regu = pt_make_regu_arith (r1, r2, NULL, T_DATE, domain);
          break;

        case PT_TIMEF:
          regu = pt_make_regu_arith (r1, r2, NULL, T_TIME, domain);
          break;

        case PT_RADIANS:
          regu = pt_make_regu_arith (r1, r2, NULL, T_RADIANS, domain);
          break;

        case PT_DEFAULTF:
          regu = pt_make_regu_arith (r1, r2, NULL, T_DEFAULT, domain);
          break;

        case PT_OID_OF_DUPLICATE_KEY:
          /* We should never get here because this function should have disappeared in pt_fold_const_expr () */
          assert (false);
          regu = NULL;
          break;

        case PT_LN:
          regu = pt_make_regu_arith (r1, r2, NULL, T_LN, domain);
          break;

        case PT_LOG2:
          regu = pt_make_regu_arith (r1, r2, NULL, T_LOG2, domain);
          break;

        case PT_LOG10:
          regu = pt_make_regu_arith (r1, r2, NULL, T_LOG10, domain);
          break;

        case PT_FORMAT:
          regu = pt_make_regu_arith (r1, r2, r3, T_FORMAT, domain);
          break;

        case PT_DATE_FORMAT:
          regu = pt_make_regu_arith (r1, r2, r3, T_DATE_FORMAT, domain);
          break;

        case PT_STR_TO_DATE:
          regu = pt_make_regu_arith (r1, r2, r3, T_STR_TO_DATE, domain);
          break;

        case PT_ADDDATE:
          regu = pt_make_regu_arith (r1, r2, NULL, T_ADDDATE, domain);
          break;

        case PT_DATEDIFF:
          regu = pt_make_regu_arith (r1, r2, NULL, T_DATEDIFF, domain);
          break;

        case PT_TIMEDIFF:
          regu = pt_make_regu_arith (r1, r2, NULL, T_TIMEDIFF, domain);
          break;

        case PT_SUBDATE:
          regu = pt_make_regu_arith (r1, r2, NULL, T_SUBDATE, domain);
          break;

        case PT_TRUNC:
          regu = pt_make_regu_arith (r1, r2, NULL, T_TRUNC, domain);
          break;

        case PT_INCR:
          regu = pt_make_regu_arith (r1, r2, r3, T_INCR, domain);
          break;

        case PT_DECR:
          regu = pt_make_regu_arith (r1, r2, r3, T_DECR, domain);
          break;

        case PT_ABS:
          regu = pt_make_regu_arith (r1, r2, NULL, T_ABS, domain);
          break;

        case PT_CHR:
          regu = pt_make_regu_arith (r1, r2, NULL, T_CHR, domain);
          break;

        case PT_INSTR:
          regu = pt_make_regu_arith (r1, r2, r3, T_INSTR, domain);
          break;

        case PT_POSITION:
          regu = pt_make_regu_arith (r1, r2, NULL, T_POSITION, domain);
          break;

        case PT_FINDINSET:
          regu = pt_make_regu_arith (r1, r2, NULL, T_FINDINSET, domain);
          break;

        case PT_SUBSTRING:
          regu = pt_make_regu_arith (r1, r2, r3, T_SUBSTRING, domain);
          pt_to_misc_operand (regu, node->info.expr.qualifier);
          break;

        case PT_SUBSTRING_INDEX:
          regu = pt_make_regu_arith (r1, r2, r3, T_SUBSTRING_INDEX, domain);
          break;

        case PT_OCTET_LENGTH:
          regu = pt_make_regu_arith (r1, r2, NULL, T_OCTET_LENGTH, domain);
          break;

        case PT_BIT_LENGTH:
          regu = pt_make_regu_arith (r1, r2, NULL, T_BIT_LENGTH, domain);
          break;

        case PT_CHAR_LENGTH:
          regu = pt_make_regu_arith (r1, r2, NULL, T_CHAR_LENGTH, domain);
          break;

        case PT_LOWER:
          regu = pt_make_regu_arith (r1, r2, NULL, T_LOWER, domain);
          break;

        case PT_UPPER:
          regu = pt_make_regu_arith (r1, r2, NULL, T_UPPER, domain);
          break;

        case PT_HEX:
          regu = pt_make_regu_arith (r1, r2, NULL, T_HEX, domain);
          break;

        case PT_ASCII:
          regu = pt_make_regu_arith (r1, r2, NULL, T_ASCII, domain);
          break;

        case PT_CONV:
          regu = pt_make_regu_arith (r1, r2, r3, T_CONV, domain);
          break;

        case PT_BIN:
          regu = pt_make_regu_arith (r1, r2, NULL, T_BIN, domain);
          break;

        case PT_MD5:
          regu = pt_make_regu_arith (r1, r2, NULL, T_MD5, domain);
          break;

        case PT_SHA_ONE:
          regu = pt_make_regu_arith (r1, r2, NULL, T_SHA_ONE, domain);
          break;

        case PT_AES_ENCRYPT:
          regu = pt_make_regu_arith (r1, r2, NULL, T_AES_ENCRYPT, domain);
          break;

        case PT_AES_DECRYPT:
          regu = pt_make_regu_arith (r1, r2, NULL, T_AES_DECRYPT, domain);
          break;

        case PT_SHA_TWO:
          regu = pt_make_regu_arith (r1, r2, NULL, T_SHA_TWO, domain);
          break;

        case PT_FROM_BASE64:
          regu = pt_make_regu_arith (r1, r2, NULL, T_FROM_BASE64, domain);
          break;

        case PT_TO_BASE64:
          regu = pt_make_regu_arith (r1, r2, NULL, T_TO_BASE64, domain);
          break;

        case PT_SPACE:
          regu = pt_make_regu_arith (r1, r2, NULL, T_SPACE, domain);
          break;

        case PT_LTRIM:
          r1 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
          r2 = (node->info.expr.arg2) ? pt_to_regu_variable (parser, node->info.expr.arg2, unbox) : NULL;
          domain = pt_xasl_node_to_domain (parser, node);
          if (domain == NULL)
            {
              break;
            }
          regu = pt_make_regu_arith (r1, r2, NULL, T_LTRIM, domain);
          break;

        case PT_RTRIM:
          r1 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
          r2 = (node->info.expr.arg2) ? pt_to_regu_variable (parser, node->info.expr.arg2, unbox) : NULL;
          domain = pt_xasl_node_to_domain (parser, node);
          if (domain == NULL)
            {
              break;
            }
          regu = pt_make_regu_arith (r1, r2, NULL, T_RTRIM, domain);
          break;

        case PT_FROM_UNIXTIME:
          r1 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
          r3 = pt_to_regu_variable (parser, node->info.expr.arg3, unbox);
          r2 = (node->info.expr.arg2) ? pt_to_regu_variable (parser, node->info.expr.arg2, unbox) : NULL;
          domain = pt_xasl_node_to_domain (parser, node);
          if (domain == NULL)
            {
              break;
            }
          regu = pt_make_regu_arith (r1, r2, r3, T_FROM_UNIXTIME, domain);
          break;

        case PT_LPAD:
          r3 = (node->info.expr.arg3) ? pt_to_regu_variable (parser, node->info.expr.arg3, unbox) : NULL;
          regu = pt_make_regu_arith (r1, r2, r3, T_LPAD, domain);
          break;

        case PT_RPAD:
          r3 = (node->info.expr.arg3) ? pt_to_regu_variable (parser, node->info.expr.arg3, unbox) : NULL;
          regu = pt_make_regu_arith (r1, r2, r3, T_RPAD, domain);
          break;

        case PT_REPLACE:
          r3 = (node->info.expr.arg3) ? pt_to_regu_variable (parser, node->info.expr.arg3, unbox) : NULL;
          regu = pt_make_regu_arith (r1, r2, r3, T_REPLACE, domain);
          break;

        case PT_TRANSLATE:
          r3 = pt_to_regu_variable (parser, node->info.expr.arg3, unbox);
          regu = pt_make_regu_arith (r1, r2, r3, T_TRANSLATE, domain);
          break;

        case PT_ADD_MONTHS:
          data_type = pt_make_prim_data_type (parser, PT_TYPE_DATE);
          domain = pt_xasl_data_type_to_domain (parser, data_type);

          regu = pt_make_regu_arith (r1, r2, NULL, T_ADD_MONTHS, domain);
          parser_free_tree (parser, data_type);
          break;

        case PT_LAST_DAY:
          data_type = pt_make_prim_data_type (parser, PT_TYPE_DATE);
          domain = pt_xasl_data_type_to_domain (parser, data_type);

          regu = pt_make_regu_arith (r1, r2, NULL, T_LAST_DAY, domain);
          parser_free_tree (parser, data_type);
          break;

        case PT_MONTHS_BETWEEN:
          data_type = pt_make_prim_data_type (parser, PT_TYPE_DOUBLE);
          domain = pt_xasl_data_type_to_domain (parser, data_type);

          regu = pt_make_regu_arith (r1, r2, NULL, T_MONTHS_BETWEEN, domain);

          parser_free_tree (parser, data_type);
          break;

        case PT_SYS_DATE:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_SYS_DATE, domain);
          break;

        case PT_CURRENT_DATE:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_CURRENT_DATE, domain);
          break;

        case PT_SYS_TIME:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_SYS_TIME, domain);
          break;

        case PT_CURRENT_TIME:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_CURRENT_TIME, domain);
          break;

        case PT_SYS_TIMESTAMP:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_SYS_TIMESTAMP, domain);
          break;

        case PT_CURRENT_TIMESTAMP:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_CURRENT_TIMESTAMP, domain);
          break;

        case PT_SYS_DATETIME:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_SYS_DATETIME, domain);
          break;

        case PT_CURRENT_DATETIME:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_CURRENT_DATETIME, domain);
          break;

        case PT_UTC_TIME:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_UTC_TIME, domain);
          break;

        case PT_UTC_DATE:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_UTC_DATE, domain);
          break;

        case PT_DBTIMEZONE:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_DBTIMEZONE, domain);
          break;

        case PT_SESSIONTIMEZONE:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_SESSIONTIMEZONE, domain);
          break;

        case PT_LOCAL_TRANSACTION_ID:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_LOCAL_TRANSACTION_ID, domain);
          break;

        case PT_CURRENT_USER:
          {
            PT_NODE *current_user_val;
            const char *username;

            username = au_get_current_user_name ();
            if (username == NULL)
              {
            PT_INTERNAL_ERROR (parser, "get user name");
            return NULL;
              }

            current_user_val = parser_new_node (parser, PT_VALUE);
            if (current_user_val == NULL)
              {
            db_string_free ((char *) username);
            PT_INTERNAL_ERROR (parser, "allocate new node");
            return NULL;
              }

            current_user_val->type_enum = PT_TYPE_VARCHAR;
            current_user_val->info.value.string_type = ' ';
            current_user_val->info.value.data_value.str = pt_append_nulstring (parser, NULL, username);
            PT_NODE_PRINT_VALUE_TO_TEXT (parser, current_user_val);

            /* copy data type (to apply collation and codeset) */
            assert (current_user_val->data_type == NULL);
            current_user_val->data_type = parser_copy_tree (parser, node->data_type);
            assert (current_user_val->data_type->type_enum == current_user_val->type_enum);

            regu = pt_to_regu_variable (parser, current_user_val, unbox);

            db_string_free ((char *) username);
            parser_free_node (parser, current_user_val);
            break;
          }
        case PT_SCHEMA_DEF:
          {
            /* cannot get here */
            assert (false);

            er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
            PT_ERRORc (parser, node, er_msg ());

            return NULL;
          }
        case PT_SCHEMA:
        case PT_USER:
          {
            char *user = NULL;
            PT_NODE *current_user_val = NULL;

            if (node->info.expr.op == PT_USER)
              {
            user = db_get_user_and_host_name ();
              }
            else
              {
            user = db_get_user_name ();
              }

            if (user == NULL)
              {
            assert (er_errid () != NO_ERROR);
            PT_INTERNAL_ERROR (parser, "get user name");

            return NULL;
              }

            current_user_val = parser_new_node (parser, PT_VALUE);
            if (current_user_val == NULL)
              {
            PT_INTERNAL_ERROR (parser, "allocate new node");
            db_private_free (NULL, user);
            return NULL;
              }
            current_user_val->type_enum = PT_TYPE_VARCHAR;
            current_user_val->info.value.string_type = ' ';

            current_user_val->info.value.data_value.str = pt_append_nulstring (parser, NULL, user);
            PT_NODE_PRINT_VALUE_TO_TEXT (parser, current_user_val);

            /* copy data type (to apply collation and codeset) */
            assert (current_user_val->data_type == NULL);
            current_user_val->data_type = parser_copy_tree (parser, node->data_type);
            assert (current_user_val->data_type->type_enum == current_user_val->type_enum);

            regu = pt_to_regu_variable (parser, current_user_val, unbox);

            db_private_free_and_init (NULL, user);
            parser_free_node (parser, current_user_val);
            break;
          }

        case PT_ROW_COUNT:
          {
            regu = pt_make_regu_arith (NULL, r1, NULL, T_ROW_COUNT, domain);
            break;
          }

        case PT_LAST_INSERT_ID:
          {
            regu = pt_make_regu_arith (NULL, r1, NULL, T_LAST_INSERT_ID, domain);
            break;
          }

        case PT_TO_CHAR:
          if (node->data_type != NULL)
            {
              data_type = node->data_type;
            }
          else
            {
              data_type = pt_make_prim_data_type (parser, PT_TYPE_VARCHAR);
              data_type->info.data_type.collation_flag = TP_DOMAIN_COLL_LEAVE;
            }
          domain = pt_xasl_data_type_to_domain (parser, data_type);

          regu = pt_make_regu_arith (r1, r2, r3, T_TO_CHAR, domain);
          if (data_type != node->data_type)
            {
              parser_free_tree (parser, data_type);
            }
          break;

        case PT_TO_DATE:
          data_type = pt_make_prim_data_type (parser, PT_TYPE_DATE);
          domain = pt_xasl_data_type_to_domain (parser, data_type);

          regu = pt_make_regu_arith (r1, r2, r3, T_TO_DATE, domain);

          parser_free_tree (parser, data_type);
          break;

        case PT_TO_TIME:
          data_type = pt_make_prim_data_type (parser, PT_TYPE_TIME);
          domain = pt_xasl_data_type_to_domain (parser, data_type);

          regu = pt_make_regu_arith (r1, r2, r3, T_TO_TIME, domain);
          parser_free_tree (parser, data_type);
          break;

        case PT_TO_TIMESTAMP:
          data_type = pt_make_prim_data_type (parser, PT_TYPE_TIMESTAMP);
          domain = pt_xasl_data_type_to_domain (parser, data_type);

          regu = pt_make_regu_arith (r1, r2, r3, T_TO_TIMESTAMP, domain);
          parser_free_tree (parser, data_type);
          break;

        case PT_TO_DATETIME:
          data_type = pt_make_prim_data_type (parser, PT_TYPE_DATETIME);
          domain = pt_xasl_data_type_to_domain (parser, data_type);

          regu = pt_make_regu_arith (r1, r2, r3, T_TO_DATETIME, domain);
          parser_free_tree (parser, data_type);
          break;

        case PT_TO_NUMBER:
          {
            int precision, scale;

            /* If 2nd argument of to_number() exists, modify domain. */
            pt_to_regu_resolve_domain (&precision, &scale, node->info.expr.arg2);
            data_type = pt_make_prim_data_type_fortonum (parser, precision, scale);

            /* create NUMERIC domain with default precision and scale. */
            domain = pt_xasl_data_type_to_domain (parser, data_type);

            /* If 2nd argument of to_number() exists, modify domain. */
            pt_to_regu_resolve_domain (&domain->precision, &domain->scale, node->info.expr.arg2);

            r3 = pt_to_regu_variable (parser, node->info.expr.arg3, unbox);

            /* Note that use the new domain */
            regu = pt_make_regu_arith (r1, r2, r3, T_TO_NUMBER, domain);
            parser_free_tree (parser, data_type);

            break;
          }

        case PT_CURRENT_VALUE:
        case PT_NEXT_VALUE:
          {
            MOP serial_mop;
            DB_VALUE dbval;
            PT_NODE *serial_obj_node_p = NULL;
            PT_NODE *cached_num_node_p = NULL;
            int cached_num;
            OPERATOR_TYPE op;

            data_type = pt_make_prim_data_type (parser, PT_TYPE_NUMERIC);
            domain = pt_xasl_data_type_to_domain (parser, data_type);

            serial_mop = pt_resolve_serial (parser, node->info.expr.arg1);
            if (serial_mop != NULL)
              {
            /* 1st regu var: serial object */
            serial_obj_node_p = parser_new_node (parser, PT_VALUE);
            if (serial_obj_node_p == NULL)
              {
                PT_INTERNAL_ERROR (parser, "allocate new node");
                return NULL;
              }

            serial_obj_node_p->type_enum = PT_TYPE_OBJECT;
            serial_obj_node_p->info.value.data_value.op = serial_mop;
            r1 = pt_to_regu_variable (parser, serial_obj_node_p, unbox);

            /* 2nd regu var: cached_num */
            if (do_get_serial_cached_num (&cached_num, serial_mop) != NO_ERROR)
              {
                PT_INTERNAL_ERROR (parser, "get serial cached_num");
                return NULL;
              }

            db_make_int (&dbval, cached_num);
            cached_num_node_p = pt_dbval_to_value (parser, &dbval);
            if (cached_num_node_p == NULL)
              {
                PT_INTERNAL_ERROR (parser, "allocate new node");
                return NULL;
              }

            r2 = pt_to_regu_variable (parser, cached_num_node_p, unbox);

            /* 3rd regu var: num_alloc */
            if (node->info.expr.op == PT_NEXT_VALUE)
              {
                r3 = pt_to_regu_variable (parser, node->info.expr.arg2, unbox);
                op = T_NEXT_VALUE;
              }
            else
              {
                r3 = NULL;

                op = T_CURRENT_VALUE;
              }

            regu = pt_make_regu_arith (r1, r2, r3, op, domain);

            parser_free_tree (parser, cached_num_node_p);
              }
            else
              {
            PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SERIAL_NOT_DEFINED,
                    node->info.expr.arg1->info.name.original);
              }

            parser_free_tree (parser, data_type);
            break;
          }

        case PT_TRIM:
          r1 = pt_to_regu_variable (parser, node->info.expr.arg1, unbox);
          r2 = (node->info.expr.arg2) ? pt_to_regu_variable (parser, node->info.expr.arg2, unbox) : NULL;
          domain = pt_xasl_node_to_domain (parser, node);
          if (domain == NULL)
            {
              break;
            }
          regu = pt_make_regu_arith (r1, r2, NULL, T_TRIM, domain);

          pt_to_misc_operand (regu, node->info.expr.qualifier);
          break;

        case PT_INST_NUM:
        case PT_ROWNUM:
        case PT_ORDERBY_NUM:
          regu = pt_make_regu_numbering (parser, node);
          break;

        case PT_LEVEL:
          regu = pt_make_regu_level (parser, node);
          break;

        case PT_CONNECT_BY_ISLEAF:
          regu = pt_make_regu_isleaf (parser, node);
          break;

        case PT_CONNECT_BY_ISCYCLE:
          regu = pt_make_regu_iscycle (parser, node);
          break;

        case PT_LEAST:
          regu = pt_make_regu_arith (r1, r2, NULL, T_LEAST, domain);
          break;

        case PT_GREATEST:
          regu = pt_make_regu_arith (r1, r2, NULL, T_GREATEST, domain);
          break;

        case PT_CAST:
          {
            OPERATOR_TYPE op;

            assert (node->node_type == PT_EXPR);
            if (PT_EXPR_INFO_IS_FLAGED (node, PT_EXPR_INFO_CAST_NOFAIL))
              {
            op = T_CAST_NOFAIL;
              }
            else
              {
            if (PT_EXPR_INFO_IS_FLAGED (node, PT_EXPR_INFO_CAST_WRAP))
              {
                op = T_CAST_WRAP;
              }
            else
              {
                op = T_CAST;
              }
              }

            if (PT_EXPR_INFO_IS_FLAGED (node, PT_EXPR_INFO_CAST_COLL_MODIFIER))
              {
            PT_NODE *arg = node->info.expr.arg1;

            domain = pt_xasl_data_type_to_domain (parser, node->info.expr.cast_type);
            assert (domain->collation_id == PT_GET_COLLATION_MODIFIER (node));
            /* COLLATE modifier eliminates extra T_CAST operator with some exceptions: 1. argument is
             * PT_NAME; attributes may be fetched from shared DB_VALUEs, and we may end up overwriting
             * collation of the same attribute used in another context; 2. argument is a normal CAST
             * (without COLLATE modifier) : normal CAST should be executed normally if that CAST is
             * changing the charset of its argument */
            if (arg != NULL
                && (arg->node_type == PT_NAME
                || (arg->node_type == PT_EXPR && arg->info.expr.op == PT_CAST
                    && !PT_EXPR_INFO_IS_FLAGED (arg, PT_EXPR_INFO_CAST_COLL_MODIFIER))))
              {
                regu = pt_make_regu_arith (r1, r2, NULL, op, domain);
              }
            else
              {
                regu = r2;
              }
            regu->domain = domain;
            if (!(arg != NULL && arg->node_type == PT_NAME && arg->type_enum == PT_TYPE_ENUMERATION))
              {
                REGU_VARIABLE_SET_FLAG (regu, REGU_VARIABLE_APPLY_COLLATION);
              }
              }
            else
              {
            domain = pt_xasl_data_type_to_domain (parser, node->info.expr.cast_type);
            regu = pt_make_regu_arith (r1, r2, NULL, op, domain);
              }
          }
          break;

        case PT_CASE:
          regu = pt_make_regu_arith (r1, r2, NULL, T_CASE, domain);
          if (regu == NULL)
            {
              break;
            }
          regu->value.arithptr->pred = pt_to_pred_expr (parser, node->info.expr.arg3);
          break;

        case PT_NULLIF:
          regu = pt_make_regu_arith (r1, r2, NULL, T_NULLIF, domain);
          break;

        case PT_COALESCE:
          regu = pt_make_regu_arith (r1, r2, NULL, T_COALESCE, domain);
          break;

        case PT_NVL:
          regu = pt_make_regu_arith (r1, r2, NULL, T_NVL, domain);
          break;

        case PT_NVL2:
          regu = pt_make_regu_arith (r1, r2, r3, T_NVL2, domain);
          break;

        case PT_DECODE:
          regu = pt_make_regu_arith (r1, r2, NULL, T_DECODE, domain);
          if (regu == NULL)
            {
              break;
            }
          regu->value.arithptr->pred = pt_to_pred_expr (parser, node->info.expr.arg3);
          break;

        case PT_EXTRACT:
          regu = pt_make_regu_arith (r1, r2, NULL, T_EXTRACT, domain);
          pt_to_misc_operand (regu, node->info.expr.qualifier);
          break;

        case PT_STRCAT:
          regu = pt_make_regu_arith (r1, r2, NULL, T_STRCAT, domain);
          break;

        case PT_SYS_CONNECT_BY_PATH:
          regu = pt_make_regu_arith (r1, r2, r3, T_SYS_CONNECT_BY_PATH, domain);
          break;

        case PT_LIST_DBS:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_LIST_DBS, domain);
          break;

        case PT_SYS_GUID:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_SYS_GUID, domain);
          break;

        case PT_BIT_TO_BLOB:
        case PT_CHAR_TO_BLOB:
          data_type = pt_make_prim_data_type (parser, PT_TYPE_BLOB);
          domain = pt_xasl_data_type_to_domain (parser, data_type);

          regu = pt_make_regu_arith (r1, NULL, NULL, T_BIT_TO_BLOB, domain);
          parser_free_tree (parser, data_type);
          break;

        case PT_BLOB_TO_BIT:
          data_type = pt_make_prim_data_type (parser, PT_TYPE_VARBIT);
          domain = pt_xasl_data_type_to_domain (parser, data_type);

          regu = pt_make_regu_arith (r1, r2, NULL, T_BLOB_TO_BIT, domain);
          parser_free_tree (parser, data_type);
          break;

        case PT_CHAR_TO_CLOB:
          data_type = pt_make_prim_data_type (parser, PT_TYPE_CLOB);
          domain = pt_xasl_data_type_to_domain (parser, data_type);

          regu = pt_make_regu_arith (r1, NULL, NULL, T_CHAR_TO_CLOB, domain);
          parser_free_tree (parser, data_type);
          break;

        case PT_CLOB_TO_CHAR:
          if (node->data_type == NULL)
            {
              data_type = pt_make_prim_data_type (parser, PT_TYPE_VARCHAR);
            }
          else
            {
              data_type = node->data_type;
            }

          domain = pt_xasl_data_type_to_domain (parser, data_type);

          regu = pt_make_regu_arith (r1, r2, NULL, T_CLOB_TO_CHAR, domain);
          break;

        case PT_BLOB_LENGTH:
        case PT_CLOB_LENGTH:
          data_type = pt_make_prim_data_type (parser, PT_TYPE_BIGINT);
          domain = pt_xasl_data_type_to_domain (parser, data_type);

          regu = pt_make_regu_arith (r1, NULL, NULL, T_LOB_LENGTH, domain);
          parser_free_tree (parser, data_type);
          break;

        case PT_TYPEOF:
          regu = pt_make_regu_arith (NULL, r2, NULL, T_TYPEOF, domain);
          break;

        case PT_INDEX_CARDINALITY:
          regu = pt_make_regu_arith (r1, r2, r3, T_INDEX_CARDINALITY, domain);
          if (parser->parent_proc_xasl != NULL)
            {
              XASL_SET_FLAG (parser->parent_proc_xasl, XASL_NO_FIXED_SCAN);
            }
          else
            {
              /* should not happen */
              assert (false);
            }
          break;

        case PT_EXEC_STATS:
          regu = pt_make_regu_arith (r1, r2, r3, T_EXEC_STATS, domain);
          break;

        case PT_TO_ENUMERATION_VALUE:
          regu = pt_make_regu_arith (NULL, r2, NULL, T_TO_ENUMERATION_VALUE, domain);
          break;

        case PT_INET_ATON:
          regu = pt_make_regu_arith (r1, r2, NULL, T_INET_ATON, domain);
          break;

        case PT_INET_NTOA:
          regu = pt_make_regu_arith (r1, r2, NULL, T_INET_NTOA, domain);
          break;

        case PT_CHARSET:
          regu = pt_make_regu_arith (r1, r2, NULL, T_CHARSET, domain);
          break;

        case PT_COLLATION:
          regu = pt_make_regu_arith (r1, r2, NULL, T_COLLATION, domain);
          break;

        case PT_WIDTH_BUCKET:
          regu = pt_make_regu_arith (r1, r2, r3, T_WIDTH_BUCKET, domain);
          break;

        case PT_TZ_OFFSET:
          regu = pt_make_regu_arith (r1, r2, NULL, T_TZ_OFFSET, domain);
          break;

        case PT_CONV_TZ:
          regu = pt_make_regu_arith (r1, r2, NULL, T_CONV_TZ, domain);
          break;

        case PT_TRACE_STATS:
          regu = pt_make_regu_arith (r1, r2, r3, T_TRACE_STATS, domain);
          break;

        case PT_INDEX_PREFIX:
          regu = pt_make_regu_arith (r1, r2, r3, T_INDEX_PREFIX, domain);
          break;

        case PT_SLEEP:
          regu = pt_make_regu_arith (r1, r2, r3, T_SLEEP, domain);
          break;

        case PT_TO_DATETIME_TZ:
          data_type = pt_make_prim_data_type (parser, PT_TYPE_DATETIMETZ);
          domain = pt_xasl_data_type_to_domain (parser, data_type);

          regu = pt_make_regu_arith (r1, r2, r3, T_TO_DATETIME_TZ, domain);
          parser_free_tree (parser, data_type);
          break;

        case PT_TO_TIMESTAMP_TZ:
          data_type = pt_make_prim_data_type (parser, PT_TYPE_TIMESTAMPTZ);
          domain = pt_xasl_data_type_to_domain (parser, data_type);

          regu = pt_make_regu_arith (r1, r2, r3, T_TO_TIMESTAMP_TZ, domain);
          parser_free_tree (parser, data_type);
          break;

        case PT_UTC_TIMESTAMP:
          regu = pt_make_regu_arith (NULL, NULL, NULL, T_UTC_TIMESTAMP, domain);
          break;

        case PT_CRC32:
          regu = pt_make_regu_arith (r1, r2, NULL, T_CRC32, domain);
          break;

        default:
          break;
        }

        end_expr_op_switch:

          if (regu && domain)
        {
          regu->domain = domain;
        }
          break;

        case PT_HOST_VAR:
          regu = pt_make_regu_hostvar (parser, node);
          break;

        case PT_NODE_LIST:
          regu = pt_make_regu_reguvalues_list (parser, node, unbox);
          break;

        case PT_VALUE:
          value = pt_value_to_db (parser, node);
          if (value)
        {
          regu = pt_make_regu_constant (parser, value, pt_node_to_db_type (node), node);
        }
          break;

        case PT_INSERT_VALUE:
          /* Create a constant regu variable using the evaluated value */
          if (!node->info.insert_value.is_evaluated)
        {
          assert (false);
          break;
        }
          value = &node->info.insert_value.value;
          regu =
        pt_make_regu_constant (parser, value, pt_node_to_db_type (node->info.insert_value.original_node),
                       node->info.insert_value.original_node);
          break;

        case PT_NAME:
          if (node->info.name.meta_class == PT_PARAMETER)
        {
          value = pt_find_value_of_label (node->info.name.original);
          if (value)
            {
              /* Note that the value in the label table will be destroyed if another assignment is made with
               * the same name ! be sure that the lifetime of this regu node will not overlap the processing of
               * another statement that may result in label assignment.  If this can happen, we'll have to copy
               * the value and remember to free it when the regu node goes away */
              regu = pt_make_regu_constant (parser, value, pt_node_to_db_type (node), node);
            }
          else
            {
              PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_DEFINED,
                  parser_print_tree (parser, node));
            }
        }
          else if (node->info.name.db_object && node->info.name.meta_class != PT_SHARED
               && node->info.name.meta_class != PT_META_ATTR && node->info.name.meta_class != PT_META_CLASS
               && node->info.name.meta_class != PT_OID_ATTR && node->info.name.meta_class != PT_CLASSOID_ATTR)
        {
          regu_alloc (val);
          pt_evaluate_tree (parser, node, val, 1);
          if (!pt_has_error (parser))
            {
              regu = pt_make_regu_constant (parser, val, pt_node_to_db_type (node), node);
            }
        }
          else
        {
          regu = pt_attribute_to_regu (parser, node);
        }

          if (regu && node->info.name.hidden_column)
        {
          REGU_VARIABLE_SET_FLAG (regu, REGU_VARIABLE_HIDDEN_COLUMN);
        }

          break;

        case PT_FUNCTION:
          regu = pt_function_to_regu (parser, node);
          break;

        case PT_SELECT:
        case PT_UNION:
        case PT_DIFFERENCE:
        case PT_INTERSECTION:
          xasl = (XASL_NODE *) node->info.query.xasl;

          if (xasl == NULL && !pt_has_error (parser))
        {
          xasl = parser_generate_xasl (parser, node);
        }

          if (xasl)
        {
          PT_NODE *select_list = pt_get_select_list (parser, node);
          if (unbox != UNBOX_AS_TABLE && pt_length_of_select_list (select_list, EXCLUDE_HIDDEN_COLUMNS) != 1)
            {
              PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_WANT_ONE_COL,
                  parser_print_tree (parser, node));
            }

          regu = pt_make_regu_subquery (parser, xasl, unbox, node);
        }
          break;

        case PT_INSERT:
          regu = pt_make_regu_insert (parser, node);
          break;

        default:
          /* force error */
          regu = NULL;
        }

      node->next = save_next;
    }

      node = save_node;     /* restore */
    }

  if (regu == NULL)
    {
      if (!pt_has_error (parser))
    {
      PT_INTERNAL_ERROR (parser, "generate var");
    }
    }

  if (val != NULL)
    {
      pr_clear_value (val);
    }

  return regu;
}

/*
 * pt_make_reserved_value_list () - Allocate an array of dbvalue pointers to
 *                  use as a cache for reserved attribute
 *                  values.
 *
 * return      : Pointer to dbvalue array.
 * parser (in) : Parser context.
 * type (in)   : Reserved name type.
 */
static DB_VALUE **
pt_make_reserved_value_list (PARSER_CONTEXT * parser, PT_RESERVED_NAME_TYPE type)
{
  DB_VALUE **value_list = NULL;
  int start = 0, end = 0, size = 0;

  PT_GET_RESERVED_NAME_FIRST_AND_LAST (type, start, end);
  size = end - start + 1;

  // *INDENT-OFF*
  regu_array_alloc<DB_VALUE *> (&value_list, size);
  // *INDENT-ON*

  if (value_list)
    {
      /* initialize values */
      for (int i = 0; i < size; ++i)
    {
      regu_alloc (value_list[i]);
      if (value_list[i] == NULL)
        {
          /* memory will be freed later */
          return NULL;
        }
    }
    }
  return value_list;
}


/*
 * pt_to_regu_variable_list () - converts a parse expression tree list
 *                               to regu_variable_list
 *   return: A NULL return indicates an error occurred
 *   parser(in):
 *   node_list(in):
 *   unbox(in):
 *   value_list(in):
 *   attr_offsets(in):
 */
static REGU_VARIABLE_LIST
pt_to_regu_variable_list (PARSER_CONTEXT * parser, PT_NODE * node_list, UNBOX unbox, VAL_LIST * value_list,
              int *attr_offsets)
{
  REGU_VARIABLE_LIST regu_list = NULL;
  REGU_VARIABLE_LIST *tail = NULL;
  REGU_VARIABLE *regu;
  PT_NODE *node;
  int i = 0;

  tail = &regu_list;

  for (node = node_list; node != NULL; node = node->next)
    {
      regu_alloc (*tail);
      regu = pt_to_regu_variable (parser, node, unbox);

      if (attr_offsets && value_list && regu)
    {
      regu->vfetch_to = pt_index_value (value_list, attr_offsets[i]);
    }
      i++;

      if (regu && *tail)
    {
      (*tail)->value = *regu;
      tail = &(*tail)->next;
    }
      else
    {
      regu_list = NULL;
      break;
    }
    }

  return regu_list;
}


/*
 * pt_regu_to_dbvalue () -
 *   return:
 *   parser(in):
 *   regu(in):
 */
static DB_VALUE *
pt_regu_to_dbvalue (PARSER_CONTEXT * parser, REGU_VARIABLE * regu)
{
  DB_VALUE *val = NULL;

  if (regu->type == TYPE_CONSTANT)
    {
      val = regu->value.dbvalptr;
    }
  else if (regu->type == TYPE_DBVAL)
    {
      val = &regu->value.dbval;
    }
  else
    {
      if (!pt_has_error (parser))
    {
      PT_INTERNAL_ERROR (parser, "generate val");
    }
    }

  return val;
}


/*
 * pt_make_position_regu_variable () - converts a parse expression tree list
 *                                     to regu_variable_list
 *   return:
 *   parser(in):
 *   node(in):
 *   i(in):
 */
static REGU_VARIABLE *
pt_make_position_regu_variable (PARSER_CONTEXT * parser, const PT_NODE * node, int i)
{
  REGU_VARIABLE *regu = NULL;
  TP_DOMAIN *domain;

  domain = pt_xasl_node_to_domain (parser, node);

  regu_alloc (regu);

  if (regu)
    {
      regu->type = TYPE_POSITION;
      regu->domain = domain;
      regu->value.pos_descr.pos_no = i;
      regu->value.pos_descr.dom = domain;
    }

  return regu;
}


/*
 * pt_make_pos_regu_var_from_scratch () - makes a position regu var from scratch
 *    return:
 *   dom(in): domain
 *   fetch_to(in): pointer to the DB_VALUE that will hold the value
 *   pos_no(in): position
 */
static REGU_VARIABLE *
pt_make_pos_regu_var_from_scratch (TP_DOMAIN * dom, DB_VALUE * fetch_to, int pos_no)
{
  REGU_VARIABLE *regu = NULL;

  regu_alloc (regu);
  if (regu)
    {
      regu->type = TYPE_POSITION;
      regu->domain = dom;
      regu->vfetch_to = fetch_to;
      regu->value.pos_descr.pos_no = pos_no;
      regu->value.pos_descr.dom = dom;
    }

  return regu;
}


/*
 * pt_to_position_regu_variable_list () - converts a parse expression tree
 *                                        list to regu_variable_list
 *   return:
 *   parser(in):
 *   node_list(in):
 *   value_list(in):
 *   attr_offsets(in):
 */
REGU_VARIABLE_LIST
pt_to_position_regu_variable_list (PARSER_CONTEXT * parser, PT_NODE * node_list, VAL_LIST * value_list,
                   int *attr_offsets)
{
  REGU_VARIABLE_LIST regu_list = NULL;
  REGU_VARIABLE_LIST *tail = NULL;
  PT_NODE *node;
  int i = 0;

  tail = &regu_list;

  for (node = node_list; node != NULL; node = node->next)
    {
      regu_alloc (*tail);

      /* it would be better form to call pt_make_position_regu_variable, but this avoids additional allocation do to
       * regu variable and regu_variable_list bizarreness. */
      if (*tail)
    {
      TP_DOMAIN *domain = pt_xasl_node_to_domain (parser, node);

      (*tail)->value.type = TYPE_POSITION;
      (*tail)->value.domain = domain;

      if (attr_offsets)
        {
          (*tail)->value.value.pos_descr.pos_no = attr_offsets[i];
        }
      else
        {
          (*tail)->value.value.pos_descr.pos_no = i;
        }

      (*tail)->value.value.pos_descr.dom = domain;

      if (value_list)
        {
          if (attr_offsets)
        {
          (*tail)->value.vfetch_to = pt_index_value (value_list, attr_offsets[i]);
        }
          else
        {
          (*tail)->value.vfetch_to = pt_index_value (value_list, i);
        }
        }

      tail = &(*tail)->next;
      i++;
    }
      else
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      regu_list = NULL;
      break;
    }
    }

  return regu_list;
}

/*
 * pt_to_regu_attr_descr () -
 *   return: int
 *   attr_descr(in): pointer to an attribute descriptor
 *   attr_id(in): attribute id
 *   type(in): attribute type
 */

static REGU_VARIABLE *
pt_to_regu_attr_descr (PARSER_CONTEXT * parser, DB_OBJECT * class_object, HEAP_CACHE_ATTRINFO * cache_attrinfo,
               PT_NODE * attr)
{
  const char *attr_name = attr->info.name.original;
  int attr_id;
  SM_DOMAIN *smdomain = NULL;
  int sharedp;
  REGU_VARIABLE *regu;
  ATTR_DESCR *attr_descr;


  if (sm_att_info (class_object, attr_name, &attr_id, &smdomain, &sharedp, attr->info.name.meta_class == PT_META_ATTR)
      != NO_ERROR)
    {
      return NULL;
    }
  if (smdomain == NULL)
    {
      return NULL;
    }
  regu_alloc (regu);
  if (regu == NULL)
    {
      return NULL;
    }

  attr_descr = &regu->value.attr_descr;
  attr_descr->reset ();

  regu->type =
    (sharedp) ? TYPE_SHARED_ATTR_ID : (attr->info.name.meta_class == PT_META_ATTR) ? TYPE_CLASS_ATTR_ID : TYPE_ATTR_ID;

  regu->domain = (TP_DOMAIN *) smdomain;
  attr_descr->id = attr_id;
  attr_descr->cache_attrinfo = cache_attrinfo;

  if (smdomain)
    {
      attr_descr->type = smdomain->type->id;
    }

  return regu;
}

/*
 * pt_to_regu_reserved_name () - Creates a regu variable for a reserved
 *               attribute.
 *
 * return      : REGU VARIABLE.
 * parser (in) : Parser Context.
 * attr (in)   : Parse tree node for a reserved attribute.
 *
 * NOTE: parser->symbols must include current_valuelist, and this regu
 *   variable will point to one of the values in that list.
 */
static REGU_VARIABLE *
pt_to_regu_reserved_name (PARSER_CONTEXT * parser, PT_NODE * attr)
{
  REGU_VARIABLE *regu = NULL;
  SYMBOL_INFO *symbols = NULL;
  int reserved_id, index;
  DB_VALUE **reserved_values = NULL;

  symbols = parser->symbols;
  assert (symbols != NULL && symbols->reserved_values != NULL);
  reserved_values = symbols->reserved_values;

  CAST_POINTER_TO_NODE (attr);
  assert (attr != NULL && attr->node_type == PT_NAME && attr->info.name.meta_class == PT_RESERVED);

  regu_alloc (regu);
  if (regu == NULL)
    {
      return NULL;
    }
  reserved_id = attr->info.name.reserved_id;
  index = pt_reserved_id_to_valuelist_index (parser, (PT_RESERVED_NAME_ID) reserved_id);
  if (index == RESERVED_NAME_INVALID)
    {
      return NULL;
    }

  /* set regu variable type */
  regu->type = TYPE_CONSTANT;

  /* set regu variable value */
  regu->value.dbvalptr = reserved_values[index];

  /* set domain */
  regu->domain = pt_xasl_node_to_domain (parser, attr);

  return regu;
}

/*
 * pt_attribute_to_regu () - Convert an attribute spec into a REGU_VARIABLE
 *   return:
 *   parser(in):
 *   attr(in):
 *
 * Note :
 * If "current_class" is non-null, use it to create a TYPE_ATTRID REGU_VARIABLE
 * Otherwise, create a TYPE_CONSTANT REGU_VARIABLE pointing to the symbol
 * table's value_list DB_VALUE, in the position matching where attr is
 * found in attribute_list.
 */
static REGU_VARIABLE *
pt_attribute_to_regu (PARSER_CONTEXT * parser, PT_NODE * attr)
{
  REGU_VARIABLE *regu = NULL;
  SYMBOL_INFO *symbols;
  DB_VALUE *dbval = NULL;
  TABLE_INFO *table_info;
  int list_index;

  CAST_POINTER_TO_NODE (attr);

  if (attr && attr->node_type == PT_NAME)
    {
      symbols = parser->symbols;
    }
  else
    {
      symbols = NULL;       /* error */
    }

  if (symbols && attr)
    {
      /* check the current scope first */
      table_info = pt_find_table_info (attr->info.name.spec_id, symbols->table_info);

      if (table_info)
    {
      /* We have found the attribute at this scope. If we had not, the attribute must have been a correlated
       * reference to an attribute at an outer scope. The correlated case is handled below in this "if" statement's
       * "else" clause. Determine if this is relative to a particular class or if the attribute should be relative
       * to the placeholder. */

      if (symbols->current_class && (table_info->spec_id == symbols->current_class->info.name.spec_id))
        {
          /* determine if this is an attribute, or an oid identifier */
          if (PT_IS_OID_NAME (attr))
        {
          regu_alloc (regu);
          if (regu)
            {
              regu->type = TYPE_OID;
              regu->domain = pt_xasl_node_to_domain (parser, attr);
            }
        }
          else if (attr->info.name.meta_class == PT_META_CLASS)
        {
          regu_alloc (regu);
          if (regu)
            {
              regu->type = TYPE_CLASSOID;
              regu->domain = pt_xasl_node_to_domain (parser, attr);
            }
        }
          else if (attr->info.name.meta_class == PT_RESERVED)
        {
          regu = pt_to_regu_reserved_name (parser, attr);
        }
          else
        {
          /* this is an attribute reference */
          if (symbols->current_class->info.name.db_object)
            {
              regu =
            pt_to_regu_attr_descr (parser, symbols->current_class->info.name.db_object,
                           symbols->cache_attrinfo, attr);
            }
          else
            {
              /* system error, we should have understood this name. */
              if (!pt_has_error (parser))
            {
              PT_INTERNAL_ERROR (parser, "generate attr");
            }
              regu = NULL;
            }
        }

          if (DB_TYPE_VOBJ == pt_node_to_db_type (attr))
        {
          regu = pt_make_vid (parser, attr->data_type, regu);
        }
        }
      else if (symbols->current_listfile
           && (list_index = pt_find_attribute (parser, attr, symbols->current_listfile)) >= 0)
        {
          /* add in the listfile attribute offset.  This is used primarily for server update and insert constraint
           * predicates because the server update prepends two columns onto the select list of the listfile. */
          list_index += symbols->listfile_attr_offset;

          if (symbols->listfile_value_list)
        {
          regu_alloc (regu);
          if (regu)
            {
              regu->domain = pt_xasl_node_to_domain (parser, attr);
              regu->type = TYPE_CONSTANT;
              dbval = pt_index_value (symbols->listfile_value_list, list_index);

              if (dbval)
            {
              regu->value.dbvalptr = dbval;
            }
              else
            {
              regu = NULL;
            }
            }
        }
          else
        {
          /* here we need the position regu variable to access the list file directly, as in list access spec
           * predicate evaluation. */
          regu = pt_make_position_regu_variable (parser, attr, list_index);
        }
        }
      else
        {
          /* Here, we are determining attribute reference information relative to the list of attribute
           * placeholders which will be fetched from the class(es). The "type" of the attribute no longer affects
           * how the placeholder is referenced. */
          regu_alloc (regu);
          if (regu)
        {
          regu->type = TYPE_CONSTANT;
          regu->domain = pt_xasl_node_to_domain (parser, attr);
          dbval =
            pt_index_value (table_info->value_list,
                    pt_find_attribute (parser, attr, table_info->attribute_list));
          if (dbval)
            {
              regu->value.dbvalptr = dbval;
            }
          else
            {
              if (PT_IS_OID_NAME (attr))
            {
              if (regu)
                {
                  regu->type = TYPE_OID;
                  regu->domain = pt_xasl_node_to_domain (parser, attr);
                }
            }
              else
            {
              regu = NULL;
            }
            }
        }
        }
    }
      else
    {
      regu_alloc (regu);
      if (regu != NULL)
        {
          /* The attribute is correlated variable. Find it in an enclosing scope(s). Note that this subquery has
           * also just been determined to be a correlated subquery. */
          REGU_VARIABLE_SET_FLAG (regu, REGU_VARIABLE_CORRELATED);
          if (symbols->stack == NULL)
        {
          if (!pt_has_error (parser))
            {
              PT_INTERNAL_ERROR (parser, "generate attr");
            }

          regu = NULL;
        }
          else
        {
          while (symbols->stack && !table_info)
            {
              symbols = symbols->stack;
              /* mark successive enclosing scopes correlated, until the attribute's "home" is found. */
              table_info = pt_find_table_info (attr->info.name.spec_id, symbols->table_info);
            }

          if (table_info)
            {
              regu->type = TYPE_CONSTANT;
              regu->domain = pt_xasl_node_to_domain (parser, attr);
              dbval =
            pt_index_value (table_info->value_list,
                    pt_find_attribute (parser, attr, table_info->attribute_list));
              if (dbval)
            {
              regu->value.dbvalptr = dbval;
            }
              else
            {
              if (PT_IS_OID_NAME (attr))
                {
                  if (regu)
                {
                  regu->type = TYPE_OID;
                  regu->domain = pt_xasl_node_to_domain (parser, attr);
                }
                }
              else
                {
                  regu = NULL;
                }
            }
            }
          else
            {
              if (!pt_has_error (parser))
            {
              PT_INTERNAL_ERROR (parser, "generate attr");
            }

              regu = NULL;
            }
        }
        }
    }
    }
  else
    {
      regu = NULL;
    }

  if (regu == NULL && !pt_has_error (parser))
    {
      const char *p = "unknown";

      if (attr)
    {
      p = attr->info.name.original;
    }

      PT_INTERNAL_ERROR (parser, "generate attr");
    }

  return regu;
}

/*
 * pt_join_term_to_regu_variable () - Translate a PT_NODE path join term
 *      to the regu_variable to follow from (left hand side of path)
 *   return:
 *   parser(in):
 *   join_term(in):
 */
static REGU_VARIABLE *
pt_join_term_to_regu_variable (PARSER_CONTEXT * parser, PT_NODE * join_term)
{
  REGU_VARIABLE *regu = NULL;

  if (join_term && join_term->node_type == PT_EXPR && join_term->info.expr.op == PT_EQ)
    {
      regu = pt_to_regu_variable (parser, join_term->info.expr.arg1, UNBOX_AS_VALUE);
    }

  return regu;
}


/*
 * op_type_to_range () -
 *   return:
 *   op_type(in):
 *   nterms(in):
 */
static RANGE
op_type_to_range (const PT_OP_TYPE op_type, const int nterms)
{
  switch (op_type)
    {
    case PT_EQ:
      return EQ_NA;
    case PT_GT:
      return (nterms > 1) ? GT_LE : GT_INF;
    case PT_GE:
      return (nterms > 1) ? GE_LE : GE_INF;
    case PT_LT:
      return (nterms > 1) ? GE_LT : INF_LT;
    case PT_LE:
      return (nterms > 1) ? GE_LE : INF_LE;
    case PT_BETWEEN:
      return GE_LE;
    case PT_EQ_SOME:
    case PT_IS_IN:
      return EQ_NA;
    case PT_BETWEEN_AND:
    case PT_BETWEEN_GE_LE:
      return GE_LE;
    case PT_BETWEEN_GE_LT:
      return GE_LT;
    case PT_BETWEEN_GT_LE:
      return GT_LE;
    case PT_BETWEEN_GT_LT:
      return GT_LT;
    case PT_BETWEEN_EQ_NA:
      return EQ_NA;
    case PT_BETWEEN_INF_LE:
      return (nterms > 1) ? GE_LE : INF_LE;
    case PT_BETWEEN_INF_LT:
      return (nterms > 1) ? GE_LT : INF_LT;
    case PT_BETWEEN_GE_INF:
      return (nterms > 1) ? GE_LE : GE_INF;
    case PT_BETWEEN_GT_INF:
      return (nterms > 1) ? GT_LE : GT_INF;
    default:
      return NA_NA;     /* error */
    }
}

/*
 * pt_create_iss_range () - Create a range to be used by Index Skip Scan
 *   return:             NO_ERROR or error code
 *   indx_infop(in,out): the index info structure that holds the special
 *                       range used by Index Skip Scan
 *   domain(in):         domain of the first range element
 *
 * Note :
 * Index Skip Scan (ISS) uses an alternative range to scan the btree for
 * the next suitable value of the first column. It looks similar to
 * "col1 > cur_col1_value". Although it is used on the server side, it must
 * be created on the broker and serialized via XASL, because the server
 * cannot create regu variables.
 * The actual range (INF_INF, GT_INF, INF_LE) will be changed dynamically
 * at runtime, as well as the comparison value (left to NULL for now),
 * but we must create the basic regu var scaffolding here.
 */
static int
pt_create_iss_range (INDX_INFO * indx_infop, TP_DOMAIN * domain)
{
  KEY_RANGE *kr = NULL;
  REGU_VARIABLE *key1 = NULL, *v1 = NULL;

  if (indx_infop == NULL)
    {
      assert (false);
      er_set (ER_FATAL_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);

      return ER_FAILED;
    }

  if (!indx_infop->use_iss)
    {
      /* nothing to do if not using iss */
      return NO_ERROR;
    }

  /* set up default range */
  kr = &indx_infop->iss_range;
  kr->range = INF_INF;

  /* allocate range lower bound as regu var (will be used on server) */
  regu_alloc (kr->key1);
  key1 = kr->key1;
  if (key1 == NULL)
    {
      return ER_FAILED;
    }

  key1->type = TYPE_FUNC;
  key1->domain = tp_domain_resolve_default (DB_TYPE_MIDXKEY);
  key1->xasl = NULL;
  key1->flags = 0;

  regu_alloc (key1->value.funcp);
  if (key1->value.funcp == NULL)
    {
      return ER_FAILED;
    }

  key1->value.funcp->ftype = F_MIDXKEY;

  regu_alloc (key1->value.funcp->operand);
  if (key1->value.funcp->operand == NULL)
    {
      return ER_FAILED;
    }

  key1->value.funcp->operand->next = NULL;

  v1 = &(key1->value.funcp->operand->value);

  v1->type = TYPE_DBVAL;

  v1->domain = domain;
  v1->flags = 0;
  db_make_null (&v1->value.dbval);

  v1->vfetch_to = NULL;

  /* upper bound is not needed */
  kr->key2 = NULL;

  return NO_ERROR;
}

/*
 * pt_to_single_key () - Create an key information(KEY_INFO) in INDX_INFO
 *      structure for index scan with range spec of R_ON, R_FROM and R_TO.
 *   return: 0 on success
 *   parser(in):
 *   term_exprs(in):
 *   nterms(in):
 *   multi_col(in):
 *   key_infop(out):
 */
static int
pt_to_single_key (PARSER_CONTEXT * parser, PT_NODE ** term_exprs, int nterms, bool multi_col, KEY_INFO * key_infop,
          int *multi_col_pos)
{
  PT_NODE *lhs, *rhs, *tmp, *midx_key;
  PT_OP_TYPE op_type;
  REGU_VARIABLE *regu_var;
  int i, pos;

  midx_key = NULL;
  regu_var = NULL;
  key_infop->key_cnt = 0;
  key_infop->key_ranges = NULL;
  key_infop->is_constant = true;

  for (i = 0; i < nterms; i++)
    {
      /* If nterms > 1, then it should be multi-column index and all term_exprs[0 .. nterms - 1] are equality
       * expression. (Even though nterms == 1, it can be multi-column index.) */

      /* op type, LHS side and RHS side of this term expression */
      op_type = term_exprs[i]->info.expr.op;
      lhs = term_exprs[i]->info.expr.arg1;
      rhs = term_exprs[i]->info.expr.arg2;

      /* incidentally we may have a term with only one range left out by pt_to_index_info(), which semantically is the
       * same as PT_EQ */
      if (op_type == PT_RANGE)
    {
      assert (PT_IS_EXPR_NODE_WITH_OPERATOR (rhs, PT_BETWEEN_EQ_NA));
      assert (rhs->or_next == NULL);

      /* has only one range */
      rhs = rhs->info.expr.arg1;
    }

      regu_var = pt_to_regu_variable (parser, rhs, UNBOX_AS_VALUE);
      if (regu_var == NULL)
    {
      goto error;
    }
      if (!VALIDATE_REGU_KEY (regu_var))
    {
      /* correlated join index case swap LHS and RHS */
      tmp = rhs;
      rhs = lhs;
      lhs = tmp;

      /* try on RHS */
      regu_var = pt_to_regu_variable (parser, rhs, UNBOX_AS_VALUE);
      if (regu_var == NULL || !VALIDATE_REGU_KEY (regu_var))
        {
          goto error;
        }
    }

      if (multi_col_pos[i] != -1)
    {
      /* case of multi column term. case1 : value type, case2 : function type */
      if (pt_is_set_type (rhs) && PT_IS_VALUE_NODE (rhs))
        {
          rhs = rhs->info.value.data_value.set;
        }
      else if (pt_is_set_type (rhs) && PT_IS_FUNCTION (rhs) && rhs->info.function.function_type == F_SEQUENCE)
        {
          rhs = rhs->info.function.arg_list;
        }
      else
        {
          /* rhs must be set type and (value or function type) */
          goto error;
        }
      if (rhs == NULL)
        {
          goto error;
        }
      for (pos = 0; pos < multi_col_pos[i]; pos++)
        {
          if (!rhs || (rhs && pt_is_set_type (rhs)))
        {
          /* must be NOT set of set */
          goto error;
        }
          rhs = rhs->next;
        }
    }
      else if (pt_is_set_type (rhs))
    {
      /* if lhs is not multi_col_term then rhs can't set type */
      goto error;
    }

      /* is the key value constant(value or host variable)? */
      key_infop->is_constant &= (rhs->node_type == PT_VALUE || rhs->node_type == PT_HOST_VAR);

      /* if it is multi-column index, make one PT_NODE for midx key value by concatenating all RHS of the terms */
      if (multi_col)
    {
      midx_key = parser_append_node (pt_point (parser, rhs), midx_key);
    }
    }               /* for (i = 0; i < nterms; i++) */

  if (midx_key)
    {
      /* make a midxkey regu variable for multi-column index */
      tmp = parser_new_node (parser, PT_FUNCTION);
      if (tmp == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      goto error;
    }
      tmp->type_enum = PT_TYPE_MIDXKEY;
      tmp->info.function.function_type = F_MIDXKEY;
      tmp->info.function.arg_list = midx_key;
      regu_var = pt_to_regu_variable (parser, tmp, UNBOX_AS_VALUE);
      parser_free_tree (parser, tmp);
      midx_key = NULL;      /* already free */
    }

  /* set KEY_INFO structure */
  key_infop->key_cnt = 1;   /* single range */
  regu_array_alloc (&key_infop->key_ranges, 1);
  if (!key_infop->key_ranges)
    {
      goto error;
    }
  key_infop->key_ranges[0].range = EQ_NA;
  key_infop->key_ranges[0].key1 = regu_var;
  key_infop->key_ranges[0].key2 = NULL;

  return 0;

/* error handling */
error:
  if (midx_key)
    {
      parser_free_tree (parser, midx_key);
    }

  return -1;
}


/*
 * pt_to_range_key () - Create an key information(KEY_INFO) in INDX_INFO
 *      structure for index scan with range spec of R_RANGE.
 *   return: 0 on success
 *   parser(in):
 *   term_exprs(in):
 *   nterms(in):
 *   multi_col(in):
 *   key_infop(out): Construct two key values
 */
static int
pt_to_range_key (PARSER_CONTEXT * parser, PT_NODE ** term_exprs, int nterms, bool multi_col, KEY_INFO * key_infop)
{
  PT_NODE *lhs, *rhs, *llim, *ulim, *tmp, *midxkey1, *midxkey2;
  PT_OP_TYPE op_type = (PT_OP_TYPE) 0;
  REGU_VARIABLE *regu_var1, *regu_var2;
  int i;

  midxkey1 = midxkey2 = NULL;
  regu_var1 = regu_var2 = NULL;
  key_infop->key_cnt = 0;
  key_infop->key_ranges = NULL;
  key_infop->is_constant = true;

  for (i = 0; i < nterms; i++)
    {
      /* If nterms > 1, then it should be multi-column index and all term_exprs[0 .. nterms - 1] are equality
       * expression. (Even though nterms == 1, it can be multi-column index.) */

      /* op type, LHS side and RHS side of this term expression */
      op_type = term_exprs[i]->info.expr.op;
      lhs = term_exprs[i]->info.expr.arg1;
      rhs = term_exprs[i]->info.expr.arg2;

      if (op_type != PT_BETWEEN)
    {
      /* PT_EQ, PT_LT, PT_LE, PT_GT, or PT_GE */

      regu_var1 = pt_to_regu_variable (parser, rhs, UNBOX_AS_VALUE);
      if (regu_var1 == NULL)
        {
          goto error;
        }
      if (!VALIDATE_REGU_KEY (regu_var1))
        {
          /* correlated join index case swap LHS and RHS */
          tmp = rhs;
          rhs = lhs;
          lhs = tmp;

          /* try on RHS */
          regu_var1 = pt_to_regu_variable (parser, rhs, UNBOX_AS_VALUE);
          if (regu_var1 == NULL || !VALIDATE_REGU_KEY (regu_var1))
        {
          goto error;
        }
          /* converse op type for the case of PT_LE, ... */
          op_type = pt_converse_op (op_type);
        }
      /* according to the 'op_type', adjust 'regu_var1' and 'regu_var2' */
      if (op_type == PT_LT || op_type == PT_LE)
        {
          /* but, 'regu_var1' and 'regu_var2' will be replaced with sequence values if it is multi-column index */
          regu_var2 = regu_var1;
          regu_var1 = NULL;
        }
      else
        {
          regu_var2 = NULL;
        }

      /* is the key value constant(value or host variable)? */
      key_infop->is_constant &= (rhs->node_type == PT_VALUE || rhs->node_type == PT_HOST_VAR);

      /* if it is multi-column index, make one PT_NODE for sequence key value by concatenating all RHS of the terms
       */
      if (multi_col)
        {
          if (op_type == PT_EQ || op_type == PT_GT || op_type == PT_GE)
        midxkey1 = parser_append_node (pt_point (parser, rhs), midxkey1);
          if (op_type == PT_EQ || op_type == PT_LT || op_type == PT_LE)
        midxkey2 = parser_append_node (pt_point (parser, rhs), midxkey2);
        }
    }
      else
    {
      /* PT_BETWEEN */
      op_type = rhs->info.expr.op;

      /* range spec(lower limit and upper limit) from operands of BETWEEN expression */
      llim = rhs->info.expr.arg1;
      ulim = rhs->info.expr.arg2;

      regu_var1 = pt_to_regu_variable (parser, llim, UNBOX_AS_VALUE);
      regu_var2 = pt_to_regu_variable (parser, ulim, UNBOX_AS_VALUE);
      if (regu_var1 == NULL || !VALIDATE_REGU_KEY (regu_var1) || regu_var2 == NULL
          || !VALIDATE_REGU_KEY (regu_var2))
        {
          goto error;
        }

      /* is the key value constant(value or host variable)? */
      key_infop->is_constant &= ((llim->node_type == PT_VALUE || llim->node_type == PT_HOST_VAR)
                     && (ulim->node_type == PT_VALUE || ulim->node_type == PT_HOST_VAR));

      /* if it is multi-column index, make one PT_NODE for sequence key value by concatenating all RHS of the terms
       */
      if (multi_col)
        {
          midxkey1 = parser_append_node (pt_point (parser, llim), midxkey1);
          midxkey2 = parser_append_node (pt_point (parser, ulim), midxkey2);
        }
    }
    }

  if (midxkey1)
    {
      /* make a midxkey regu variable for multi-column index */
      tmp = parser_new_node (parser, PT_FUNCTION);
      if (tmp == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      goto error;
    }
      tmp->type_enum = PT_TYPE_MIDXKEY;
      tmp->info.function.function_type = F_MIDXKEY;
      tmp->info.function.arg_list = midxkey1;
      regu_var1 = pt_to_regu_variable (parser, tmp, UNBOX_AS_VALUE);
      parser_free_tree (parser, tmp);
      midxkey1 = NULL;      /* already free */
    }

  if (midxkey2)
    {
      /* make a midxkey regu variable for multi-column index */
      tmp = parser_new_node (parser, PT_FUNCTION);
      if (tmp == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return -1;
    }
      tmp->type_enum = PT_TYPE_MIDXKEY;
      tmp->info.function.function_type = F_MIDXKEY;
      tmp->info.function.arg_list = midxkey2;
      regu_var2 = pt_to_regu_variable (parser, tmp, UNBOX_AS_VALUE);
      parser_free_tree (parser, tmp);
      midxkey2 = NULL;      /* already free */
    }

  /* set KEY_INFO structure */
  key_infop->key_cnt = 1;   /* single range */
  regu_array_alloc (&key_infop->key_ranges, 1);
  if (!key_infop->key_ranges)
    {
      goto error;
    }
  key_infop->key_ranges[0].range = op_type_to_range (op_type, nterms);
  key_infop->key_ranges[0].key1 = regu_var1;
  key_infop->key_ranges[0].key2 = regu_var2;

  return 0;

/* error handling */
error:

  if (midxkey1)
    {
      parser_free_tree (parser, midxkey1);
    }
  if (midxkey2)
    {
      parser_free_tree (parser, midxkey2);
    }

  return -1;
}

/*
 * pt_to_list_key () - Create an key information(KEY_INFO) in INDX_INFO
 *  structure for index scan with range spec of R_LIST
 *   return: 0 on success
 *   parser(in):
 *   term_exprs(in):
 *   nterms(in):
 *   multi_col(in):
 *   key_infop(out): Construct a list of key values
 */
static int
pt_to_list_key (PARSER_CONTEXT * parser, PT_NODE ** term_exprs, int nterms, bool multi_col, KEY_INFO * key_infop)
{
  PT_NODE *lhs, *rhs, *elem, *tmp, **midxkey_list;
  PT_OP_TYPE op_type;
  REGU_VARIABLE **regu_var_list, *regu_var;
  int i, j, n_elem;
  DB_VALUE db_value, *p;
  DB_COLLECTION *db_collectionp = NULL;

  midxkey_list = NULL;
  regu_var_list = NULL;
  key_infop->key_cnt = 0;
  key_infop->key_ranges = NULL;
  key_infop->is_constant = true;
  n_elem = 0;

  /* get number of elements of the IN predicate */
  rhs = term_exprs[nterms - 1]->info.expr.arg2;

  if (rhs->node_type == PT_EXPR && rhs->info.expr.op == PT_CAST)
    {
      /* strip CAST operator off */
      rhs = rhs->info.expr.arg1;
    }

  switch (rhs->node_type)
    {
    case PT_FUNCTION:
      switch (rhs->info.function.function_type)
    {
    case F_SET:
    case F_MULTISET:
    case F_SEQUENCE:
      break;
    default:
      goto error;
    }

      for (elem = rhs->info.function.arg_list, n_elem = 0; elem; elem = elem->next, n_elem++)
    {
      ;
    }
      break;

    case PT_NAME:
      if (rhs->info.name.meta_class != PT_PARAMETER)
    {
      goto error;
    }
      [[fallthrough]];

    case PT_VALUE:
      p = (rhs->node_type == PT_NAME) ? pt_find_value_of_label (rhs->info.name.original) : &rhs->info.value.db_value;

      if (p == NULL)
    {
      goto error;
    }

      switch (DB_VALUE_TYPE (p))
    {
    case DB_TYPE_SET:
    case DB_TYPE_MULTISET:
    case DB_TYPE_SEQUENCE:
      break;
    default:
      goto error;
    }

      db_collectionp = db_get_collection (p);
      n_elem = db_col_size (db_collectionp);
      break;

    case PT_HOST_VAR:
      p = pt_value_to_db (parser, rhs);
      if (p == NULL)
    {
      goto error;
    }

      switch (DB_VALUE_TYPE (p))
    {
    case DB_TYPE_SET:
    case DB_TYPE_MULTISET:
    case DB_TYPE_SEQUENCE:
      break;
    default:
      goto error;
    }

      db_collectionp = db_get_collection (p);
      n_elem = db_col_size (db_collectionp);
      break;

    default:
      goto error;
    }

  if (n_elem <= 0)
    {
      goto error;
    }

  /* allocate regu variable list and sequence value list */
  regu_array_alloc (&regu_var_list, n_elem);
  if (!regu_var_list)
    {
      goto error;
    }

  if (multi_col)
    {
      midxkey_list = (PT_NODE **) malloc (sizeof (PT_NODE *) * n_elem);
      if (!midxkey_list)
    {
      goto error;
    }
      memset (midxkey_list, 0, sizeof (PT_NODE *) * n_elem);
    }

  for (i = 0; i < nterms; i++)
    {
      /* If nterms > 1, then it should be multi-column index and all term_exprs[0 .. nterms - 1] are equality
       * expression. (Even though nterms == 1, it can be multi-column index.) */

      /* op type, LHS side and RHS side of this term expression */
      op_type = term_exprs[i]->info.expr.op;
      lhs = term_exprs[i]->info.expr.arg1;
      rhs = term_exprs[i]->info.expr.arg2;

      if (op_type != PT_IS_IN && op_type != PT_EQ_SOME)
    {
      /* PT_EQ */

      regu_var = pt_to_regu_variable (parser, rhs, UNBOX_AS_VALUE);
      if (regu_var == NULL)
        {
          goto error;
        }
      if (!VALIDATE_REGU_KEY (regu_var))
        {
          /* correlated join index case swap LHS and RHS */
          tmp = rhs;
          rhs = lhs;
          lhs = tmp;

          /* try on RHS */
          regu_var = pt_to_regu_variable (parser, rhs, UNBOX_AS_VALUE);
          if (regu_var == NULL || !VALIDATE_REGU_KEY (regu_var))
        {
          goto error;
        }
        }

      /* is the key value constant(value or host variable)? */
      key_infop->is_constant &= (rhs->node_type == PT_VALUE || rhs->node_type == PT_HOST_VAR);

      /* if it is multi-column index, make one PT_NODE for sequence key value by concatenating all RHS of the terms
       */
      if (multi_col)
        {
          for (j = 0; j < n_elem; j++)
        {
          midxkey_list[j] = parser_append_node (pt_point (parser, rhs), midxkey_list[j]);
        }
        }

    }
      else
    {
      /* PT_IS_IN or PT_EQ_SOME */

      if (rhs->node_type == PT_FUNCTION || rhs->node_type == PT_METHOD_CALL)
        {
          /* PT_FUNCTION */
          elem = (rhs->node_type == PT_FUNCTION) ? rhs->info.function.arg_list : rhs->info.method_call.arg_list;

          for (j = 0; j < n_elem && elem; j++, elem = elem->next)
        {

          regu_var_list[j] = pt_to_regu_variable (parser, elem, UNBOX_AS_VALUE);
          if (regu_var_list[j] == NULL || !VALIDATE_REGU_KEY (regu_var_list[j]))
            {
              goto error;
            }

          /* is the key value constant(value or host variable)? */
          key_infop->is_constant &= (elem->node_type == PT_VALUE || elem->node_type == PT_HOST_VAR);

          /* if it is multi-column index, make one PT_NODE for sequence key value by concatenating all RHS of
           * the terms */
          if (multi_col)
            {
              midxkey_list[j] = parser_append_node (pt_point (parser, elem), midxkey_list[j]);
            }
        }       /* for (j = 0, = ...) */
        }
      else
        {
          /* PT_NAME or PT_VALUE */
          for (j = 0; j < n_elem; j++)
        {
          if (db_col_get (db_collectionp, j, &db_value) < 0)
            {
              goto error;
            }
          elem = pt_dbval_to_value (parser, &db_value);
          if (elem == NULL)
            {
              goto error;
            }
          pr_clear_value (&db_value);

          regu_var_list[j] = pt_to_regu_variable (parser, elem, UNBOX_AS_VALUE);
          if (regu_var_list[j] == NULL || !VALIDATE_REGU_KEY (regu_var_list[j]))
            {
              parser_free_tree (parser, elem);
              goto error;
            }

          /* if it is multi-column index, make one PT_NODE for midxkey value by concatenating all RHS of the
           * terms */
          if (multi_col)
            {
              midxkey_list[j] = parser_append_node (elem, midxkey_list[j]);
            }
        }
        }
    }
    }

  if (multi_col)
    {
      /* make a midxkey regu variable for multi-column index */
      for (i = 0; i < n_elem; i++)
    {
      if (!midxkey_list[i])
        {
          goto error;
        }

      tmp = parser_new_node (parser, PT_FUNCTION);
      if (tmp == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          goto error;
        }
      tmp->type_enum = PT_TYPE_MIDXKEY;
      tmp->info.function.function_type = F_MIDXKEY;
      tmp->info.function.arg_list = midxkey_list[i];
      regu_var_list[i] = pt_to_regu_variable (parser, tmp, UNBOX_AS_VALUE);
      parser_free_tree (parser, tmp);
      midxkey_list[i] = NULL;   /* already free */
    }
    }

  /* set KEY_INFO structure */
  key_infop->key_cnt = n_elem;  /* n_elem ranges */
  regu_array_alloc (&key_infop->key_ranges, n_elem);
  if (!key_infop->key_ranges)
    {
      goto error;
    }
  for (i = 0; i < n_elem; i++)
    {
      key_infop->key_ranges[i].range = EQ_NA;
      key_infop->key_ranges[i].key1 = regu_var_list[i];
      key_infop->key_ranges[i].key2 = NULL;
    }

  if (midxkey_list)
    {
      free_and_init (midxkey_list);
    }

  return 0;

/* error handling */
error:

  if (midxkey_list)
    {
      for (i = 0; i < n_elem; i++)
    {
      if (midxkey_list[i])
        {
          parser_free_tree (parser, midxkey_list[i]);
        }
    }
      free_and_init (midxkey_list);
    }

  return -1;
}


/*
 * pt_to_rangelist_key () - Create an key information(KEY_INFO) in INDX_INFO
 *  structure for index scan with range spec of R_RANGELIST
 *   return:
 *   parser(in):
 *   term_exprs(in):
 *   nterms(in):
 *   multi_col(in):
 *   key_infop(out): Construct a list of search range values
 *   rangelist_idx(in):
 */
static int
pt_to_rangelist_key (PARSER_CONTEXT * parser, PT_NODE ** term_exprs, int nterms, bool multi_col, KEY_INFO * key_infop,
             int rangelist_idx, int *multi_col_pos)
{
  PT_NODE *lhs, *rhs, *llim, *ulim, *elem, *tmp, *elem2;
  PT_NODE **midxkey_list1 = NULL, **midxkey_list2 = NULL;
  PT_OP_TYPE op_type;
  REGU_VARIABLE **regu_var_list1, **regu_var_list2, *regu_var;
  RANGE *range_list = NULL;
  int i, j, n_elem, pos;
  int list_count1, list_count2;
  int num_index_term;

  midxkey_list1 = midxkey_list2 = NULL;
  regu_var_list1 = regu_var_list2 = NULL;
  key_infop->key_cnt = 0;
  key_infop->key_ranges = NULL;
  key_infop->is_constant = true;
  n_elem = 0;

  /* get number of elements of the RANGE predicate */
  rhs = term_exprs[rangelist_idx]->info.expr.arg2;
  for (elem = rhs, n_elem = 0; elem; elem = elem->or_next, n_elem++)
    {
      ;
    }
  if (n_elem <= 0)
    {
      goto error;
    }

  /* allocate regu variable list and sequence value list */
  regu_array_alloc (&regu_var_list1, n_elem);
  regu_array_alloc (&regu_var_list2, n_elem);
  range_list = (RANGE *) malloc (sizeof (RANGE) * n_elem);
  if (!regu_var_list1 || !regu_var_list2 || !range_list)
    {
      goto error;
    }

  memset (range_list, 0, sizeof (RANGE) * n_elem);

  if (multi_col)
    {
      midxkey_list1 = (PT_NODE **) malloc (sizeof (PT_NODE *) * n_elem);
      if (midxkey_list1 == NULL)
    {
      goto error;
    }
      memset (midxkey_list1, 0, sizeof (PT_NODE *) * n_elem);

      midxkey_list2 = (PT_NODE **) malloc (sizeof (PT_NODE *) * n_elem);
      if (midxkey_list2 == NULL)
    {
      goto error;
    }
      memset (midxkey_list2, 0, sizeof (PT_NODE *) * n_elem);
    }

  /* for each term */
  for (i = 0; i < nterms; i++)
    {
      /* If nterms > 1, then it should be multi-column index and all term_expr[0 .. nterms - 1] are equality
       * expression. (Even though nterms == 1, it can be multi-column index.) */

      /* op type, LHS side and RHS side of this term expression */
      op_type = term_exprs[i]->info.expr.op;
      lhs = term_exprs[i]->info.expr.arg1;
      rhs = term_exprs[i]->info.expr.arg2;

      llim = ulim = NULL;   /* init */

      if (op_type != PT_RANGE)
    {
      assert (i != rangelist_idx);

      /* PT_EQ */

      regu_var = pt_to_regu_variable (parser, rhs, UNBOX_AS_VALUE);
      if (regu_var == NULL)
        {
          goto error;
        }
      if (!VALIDATE_REGU_KEY (regu_var))
        {
          /* correlated join index case swap LHS and RHS */
          tmp = rhs;
          rhs = lhs;
          lhs = tmp;

          /* try on RHS */
          regu_var = pt_to_regu_variable (parser, rhs, UNBOX_AS_VALUE);
          if (regu_var == NULL || !VALIDATE_REGU_KEY (regu_var))
        {
          goto error;
        }
        }

      /* is the key value constant(value or host variable)? */
      key_infop->is_constant &= (rhs->node_type == PT_VALUE || rhs->node_type == PT_HOST_VAR);

      for (j = 0; j < n_elem; j++)
        {
          if (i == nterms - 1)
        {       /* the last term */
          range_list[j] = op_type_to_range (op_type, nterms);
        }

          /* if it is multi-column index, make one PT_NODE for sequence key value by concatenating all RHS of the
           * terms */
          if (multi_col)
        {
          midxkey_list1[j] = parser_append_node (pt_point (parser, rhs), midxkey_list1[j]);
          midxkey_list2[j] = parser_append_node (pt_point (parser, rhs), midxkey_list2[j]);
        }
        }
    }
      else
    {
      assert ((i == rangelist_idx) || (i != rangelist_idx && rhs->or_next == NULL) || multi_col_pos[i] != -1);

      /* PT_RANGE */
      for (j = 0, elem = rhs; j < n_elem && elem; j++, elem = elem->or_next)
        {
          /* range type and spec(lower limit and upper limit) from operands of RANGE expression */
          op_type = elem->info.expr.op;
          switch (op_type)
        {
        case PT_BETWEEN_EQ_NA:
          llim = elem->info.expr.arg1;
          ulim = llim;
          break;
        case PT_BETWEEN_INF_LE:
        case PT_BETWEEN_INF_LT:
          llim = NULL;
          ulim = elem->info.expr.arg1;
          break;
        case PT_BETWEEN_GE_INF:
        case PT_BETWEEN_GT_INF:
          llim = elem->info.expr.arg1;
          ulim = NULL;
          break;
        default:
          llim = elem->info.expr.arg1;
          ulim = elem->info.expr.arg2;
          break;
        }

          if (multi_col_pos[i] != -1)
        {
          /* case of multi column term. case1 : value type, case2 : function type */
          if (pt_is_set_type (llim) && pt_is_value_node (llim))
            {
              llim = llim->info.value.data_value.set;
            }
          else if (pt_is_set_type (llim) && pt_is_function (llim)
               && llim->info.function.function_type == F_SEQUENCE)
            {
              llim = llim->info.function.arg_list;
            }
          else
            {
              /* rhs must be set type and (value or function type) */
              goto error;
            }
          ulim = llim;
          for (pos = 0; pos < multi_col_pos[i]; pos++)
            {
              if (!llim || (llim && pt_is_set_type (llim)))
            {
              /* must be NOT set of set */
              goto error;
            }
              llim = llim->next;
              ulim = ulim->next;
            }
        }
          else if (pt_is_set_type (llim))
        {
          /* if lhs is not multi_col_term then rhs can't set type */
          goto error;
        }

          if (llim)
        {
          regu_var_list1[j] = pt_to_regu_variable (parser, llim, UNBOX_AS_VALUE);
          if (regu_var_list1[j] == NULL || !VALIDATE_REGU_KEY (regu_var_list1[j]))
            {
              goto error;
            }

          /* is the key value constant(value or host variable)? */
          key_infop->is_constant &= (llim->node_type == PT_VALUE || llim->node_type == PT_HOST_VAR);
        }
          else
        {
          regu_var_list1[j] = NULL;
        }       /* if (llim) */

          if (ulim)
        {
          regu_var_list2[j] = pt_to_regu_variable (parser, ulim, UNBOX_AS_VALUE);
          if (regu_var_list2[j] == NULL || !VALIDATE_REGU_KEY (regu_var_list2[j]))
            {
              goto error;
            }

          /* is the key value constant(value or host variable)? */
          key_infop->is_constant &= (ulim->node_type == PT_VALUE || ulim->node_type == PT_HOST_VAR);
        }
          else
        {
          regu_var_list2[j] = NULL;
        }       /* if (ulim) */

          if (i == nterms - 1)
        {       /* the last term */
          range_list[j] = op_type_to_range (op_type, nterms);
        }

          /* if it is multi-column index, make one PT_NODE for sequence key value by concatenating all RHS of the
           * terms */
          if (multi_col)
        {
          if (llim)
            {
              midxkey_list1[j] = parser_append_node (pt_point (parser, llim), midxkey_list1[j]);
            }
          if (ulim)
            {
              midxkey_list2[j] = parser_append_node (pt_point (parser, ulim), midxkey_list2[j]);
            }
        }
        }           /* for (j = 0, elem = rhs; ... ) */

      if (multi_col_pos[i] != -1 || i == rangelist_idx)
        {
          assert (j == n_elem);
          /* OK; nop */
        }
      else
        {
          int k;

          assert (j == 1);

          for (k = j; k < n_elem; k++)
        {
          if (i == nterms - 1)
            {       /* the last term */
              range_list[k] = op_type_to_range (op_type, nterms);
            }

          if (multi_col)
            {
              if (llim)
            {
              midxkey_list1[k] = parser_append_node (pt_point (parser, llim), midxkey_list1[k]);
            }
              if (ulim)
            {
              midxkey_list2[k] = parser_append_node (pt_point (parser, ulim), midxkey_list2[k]);
            }
            }
        }       /* for */
        }
    }           /* else (op_type != PT_RANGE) */
    }               /* for (i = 0; i < nterms; i++) */

  if (multi_col)
    {
      /* make a midxkey regu variable for multi-column index */
      for (i = 0; i < n_elem; i++)
    {
      if (midxkey_list1[i])
        {
          tmp = parser_new_node (parser, PT_FUNCTION);
          if (tmp == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          goto error;
        }

          tmp->type_enum = PT_TYPE_MIDXKEY;
          tmp->info.function.function_type = F_MIDXKEY;
          tmp->info.function.arg_list = midxkey_list1[i];
          regu_var_list1[i] = pt_to_regu_variable (parser, tmp, UNBOX_AS_VALUE);
          parser_free_tree (parser, tmp);
          midxkey_list1[i] = NULL;  /* already free */
        }
    }
      free_and_init (midxkey_list1);

      /* make a midxkey regu variable for multi-column index */
      for (i = 0; i < n_elem; i++)
    {
      if (midxkey_list2[i])
        {
          tmp = parser_new_node (parser, PT_FUNCTION);
          if (tmp == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          goto error;
        }
          tmp->type_enum = PT_TYPE_MIDXKEY;
          tmp->info.function.function_type = F_MIDXKEY;
          tmp->info.function.arg_list = midxkey_list2[i];
          regu_var_list2[i] = pt_to_regu_variable (parser, tmp, UNBOX_AS_VALUE);
          parser_free_tree (parser, tmp);
          midxkey_list2[i] = NULL;  /* already free */
        }
    }
      free_and_init (midxkey_list2);
    }

  /* assertion block */
  if (multi_col == true)
    {
      REGU_VARIABLE_LIST requ_list;

      num_index_term = 0;   /* to make compiler be silent */
      for (i = 0; i < n_elem; i++)
    {
      list_count1 = list_count2 = 0;

      if (regu_var_list1[i] != NULL)
        {
          for (requ_list = regu_var_list1[i]->value.funcp->operand; requ_list; requ_list = requ_list->next)
        {
          list_count1++;
        }
        }

      if (regu_var_list2[i] != NULL)
        {
          for (requ_list = regu_var_list2[i]->value.funcp->operand; requ_list; requ_list = requ_list->next)
        {
          list_count2++;
        }
        }

      if (i == 0)
        {
          num_index_term = MAX (list_count1, list_count2);
        }
      else
        {
          if (num_index_term != MAX (list_count1, list_count2))
        {
          assert_release (0);
          goto error;
        }
        }
    }
    }

  /* set KEY_INFO structure */
  key_infop->key_cnt = n_elem;  /* n_elem ranges */
  regu_array_alloc (&key_infop->key_ranges, n_elem);
  if (!key_infop->key_ranges)
    {
      goto error;
    }

  for (i = 0; i < n_elem; i++)
    {
      key_infop->key_ranges[i].range = range_list[i];
      key_infop->key_ranges[i].key1 = regu_var_list1[i];
      key_infop->key_ranges[i].key2 = regu_var_list2[i];
    }

  if (range_list)
    {
      free_and_init (range_list);
    }

  return 0;

/* error handling */
error:

  if (midxkey_list1)
    {
      for (i = 0; i < n_elem; i++)
    {
      if (midxkey_list1[i])
        {
          parser_free_tree (parser, midxkey_list1[i]);
        }
    }
      free_and_init (midxkey_list1);
    }
  if (midxkey_list2)
    {
      for (i = 0; i < n_elem; i++)
    {
      if (midxkey_list2[i])
        {
          parser_free_tree (parser, midxkey_list2[i]);
        }
    }
      free_and_init (midxkey_list2);
    }

  if (range_list)
    {
      free_and_init (range_list);
    }

  return -1;
}


/*
 * pt_to_key_limit () - Create index key limit regu variables
 *   return:
 *   parser(in):
 *   key_limit(in):
 *   key_infop(in):
 *   key_limit_reset(in);
 */
static int
pt_to_key_limit (PARSER_CONTEXT * parser, PT_NODE * key_limit, QO_LIMIT_INFO * limit_infop, KEY_INFO * key_infop,
         bool key_limit_reset)
{
  REGU_VARIABLE *regu_var_u = NULL, *regu_var_l = NULL;
  PT_NODE *limit_u, *limit_l;
  TP_DOMAIN *dom_bigint = tp_domain_resolve_default (DB_TYPE_BIGINT);

  /* at least one of them should be NULL, although they both can */
  assert (key_limit == NULL || limit_infop == NULL);

  limit_u = key_limit;
  if (limit_u != NULL)
    {
      /* user explicitly specifies keylimit */
      key_infop->is_user_given_keylimit = true;

      if (limit_u->type_enum == PT_TYPE_MAYBE)
    {
      limit_u->expected_domain = dom_bigint;
    }
      regu_var_u = pt_to_regu_variable (parser, limit_u, UNBOX_AS_VALUE);
      if (regu_var_u == NULL)
    {
      goto error;
    }

      limit_l = limit_u->next;
      if (limit_l != NULL)
    {
      if (limit_l->type_enum == PT_TYPE_MAYBE)
        {
          limit_l->expected_domain = dom_bigint;
        }
      regu_var_l = pt_to_regu_variable (parser, limit_l, UNBOX_AS_VALUE);
      if (regu_var_l == NULL)
        {
          goto error;
        }
    }
    }

  if (limit_infop != NULL)
    {
      regu_var_u = limit_infop->upper;
      regu_var_l = limit_infop->lower;
    }

  if (key_infop->key_limit_u != NULL)
    {
      if (regu_var_u != NULL)
    {
      key_infop->key_limit_u = pt_make_regu_arith (key_infop->key_limit_u, regu_var_u, NULL, T_LEAST, dom_bigint);
      if (key_infop->key_limit_u == NULL)
        {
          goto error;
        }
      key_infop->key_limit_u->domain = dom_bigint;
    }
    }
  else
    {
      key_infop->key_limit_u = regu_var_u;
    }

  if (key_infop->key_limit_l != NULL)
    {
      if (regu_var_l != NULL)
    {
      key_infop->key_limit_l =
        pt_make_regu_arith (key_infop->key_limit_l, regu_var_l, NULL, T_GREATEST, dom_bigint);
      if (key_infop->key_limit_l == NULL)
        {
          goto error;
        }
      key_infop->key_limit_l->domain = dom_bigint;
    }
    }
  else
    {
      key_infop->key_limit_l = regu_var_l;
    }

  key_infop->key_limit_reset = key_limit_reset;

  return NO_ERROR;

error:

  return ER_FAILED;
}


/*
 * pt_instnum_to_key_limit () - try to convert instnum to keylimit
 *   return:
 *   parser(in):
 *   plan(in):
 *   xasl(in):
 */
static int
pt_instnum_to_key_limit (PARSER_CONTEXT * parser, QO_PLAN * plan, XASL_NODE * xasl)
{
  XASL_NODE *xptr;
  ACCESS_SPEC_TYPE *spec_list;
  QO_LIMIT_INFO *limit_infop;
  int ret = NO_ERROR;

  /* If ANY of the spec lists has a data filter, we cannot convert instnum to keylimit, because in the worst case
   * scenario the data filter could require all the records in a join operation and chose only the last Cartesian
   * tuple, and any keylimit on the joined tables would be wrong. */
  for (xptr = xasl; xptr; xptr = xptr->scan_ptr)
    {
      for (spec_list = xptr->spec_list; spec_list; spec_list = spec_list->next)
    {
      if (spec_list->where_pred)
        {
          /* this is not an error, just halt the optimization tentative */
          return NO_ERROR;
        }
    }
    }

  /* if there is an orderby_num pred, meaning order by was not skipped */
  if (xasl->ordbynum_pred || xasl->if_pred || xasl->after_join_pred)
    {
      /* can't optimize */
      return NO_ERROR;
    }

  /* halt if there is connect by */
  if (xasl->connect_by_ptr)
    {
      return NO_ERROR;
    }

  /* if there are analytic function and instnum, can't optimize */
  if (xasl->instnum_flag & XASL_INSTNUM_FLAG_SCAN_STOP_AT_ANALYTIC)
    {
      return NO_ERROR;
    }

  limit_infop = qo_get_key_limit_from_instnum (parser, plan, xasl);
  if (!limit_infop)
    {
      return NO_ERROR;
    }
  if (!limit_infop->upper)
    {
      db_private_free (NULL, limit_infop);
      return NO_ERROR;
    }


  /* there is at least an upper limit, but we need to take some decisions depending on the presence of a lower limit
   * and the query complexity */

  /* do we have a join or other non-trivial select? */
  if (xasl->scan_ptr)
    {
      /* If we are joining multiple tables, we cannot afford to use the lower limit: it should be applied only at the
       * higher, join level and not at lower table scan levels. Discard the lower limit. */
      limit_infop->lower = NULL;
    }
  else if (plan->plan_type == QO_PLANTYPE_SCAN && QO_NODE_IS_CLASS_HIERARCHY (plan->plan_un.scan.node))
    {
      /* We cannot use the lower limit in a hierarchy */
      limit_infop->lower = NULL;
    }
  else
    {
      /* a trivial select: we can keep the lower limit, but we must adjust the upper limit.
       * qo_get_key_limit_from_instnum gets a lower and an upper limit, but keylimit requires a min and a count (i.e.
       * how many should we skip, and then how many should we fetch) */
      if (limit_infop->lower)
    {
      TP_DOMAIN *dom_bigint = tp_domain_resolve_default (DB_TYPE_BIGINT);

      limit_infop->upper = pt_make_regu_arith (limit_infop->upper, limit_infop->lower, NULL, T_SUB, dom_bigint);
      if (limit_infop->upper == NULL)
        {
          goto exit_on_error;
        }

      limit_infop->upper->domain = dom_bigint;
    }

      /* we must also delete the instnum predicate, because we don't want two sets of lower limits for the same data */
      assert (xasl->instnum_pred);
      xasl->instnum_pred = NULL;
    }

  /* cannot handle for join; skip and go ahead */
  if (xasl->scan_ptr == NULL)
    {
      /* set the key limit to all the eligible spec lists (the ones that have index scans.) */
      for (xptr = xasl; xptr; xptr = xptr->scan_ptr)
    {
      for (spec_list = xptr->spec_list; spec_list; spec_list = spec_list->next)
        {
          if (!spec_list->indexptr)
        {
          continue;
        }

          ret = pt_to_key_limit (parser, NULL, limit_infop, &(spec_list->indexptr->key_info), false);
          if (ret != NO_ERROR)
        {
          goto exit_on_error;
        }
        }
    }
    }

  /* we're done with the generated key limit tree */
  db_private_free (NULL, limit_infop);

  return NO_ERROR;

exit_on_error:
  if (limit_infop)
    {
      db_private_free (NULL, limit_infop);
    }
  return ER_FAILED;
}


/*
 * pt_fix_first_term_expr_for_iss () - allocates needed first term for the
 *                                  index skip scan optimization (ISS)
 *   return:
 *   parser(in):
 *   index_entryp(in):
 *   term_exprs(in):
 *
 * Notes:
 * ISS involves using the index when there is no condition (term) on the first
 * column of the index, by performing multiple btree_range_search() calls, one
 * for each value of the first column that exists in the database.
 * For instance for an index on c1,c2,c3 and the condition
 * ... WHERE C2 = 5 and C3 > 10, we will have to generate a range similar to
 * [C1=?, C2=5, C3 > 10] and fill up the "?" at each successive run with a
 * new value for C1. This is taken care of on the server, in
 * obtain_next_iss_value() & co. However, the code that generates the range,
 * here in XASL generation, assumes that there ARE terms for all of C1,C2, C3.
 *
 * Therefore, we must generate a "fake" term [C1=?], and just allow the range
 * generation code to do its job, knowing that once it gets to the server
 * side, we will replace the "?" with proper values for C1.
 * The term that is added is harmless (i.e. it does not show up when printing
 * the tree etc).
 */
static int
pt_fix_first_term_expr_for_iss (PARSER_CONTEXT * parser, QO_INDEX_ENTRY * index_entryp, PT_NODE ** term_exprs)
{
  PT_NODE *expr = NULL;
  PT_NODE *arg = NULL;
  PT_NODE *val = NULL;
  PT_NODE *spec;
  QO_SEGMENT *seg;
  QO_NODE *head;
  DB_VALUE temp_null;

  /* better than leaving it uninitialized */
  term_exprs[0] = NULL;

  if (index_entryp->nsegs <= 1 || index_entryp->seg_idxs[1] == -1)
    {
      assert (index_entryp->nsegs > 1 && index_entryp->seg_idxs[1] != -1);
      goto exit_error;
    }

  if (index_entryp->constraints->func_index_info && index_entryp->constraints->func_index_info->col_id == 0)
    {
      if (pt_fix_first_term_func_index_for_iss (parser, index_entryp, term_exprs) != NO_ERROR)
    {
      goto exit_error;
    }
      return NO_ERROR;
    }

  arg = pt_name (parser, index_entryp->constraints->attributes[0]->header.name);
  if (arg == NULL)
    {
      goto exit_error;
    }

  /* SEG IDXS [1] is the SECOND column of the index */
  seg = QO_ENV_SEG (index_entryp->terms.env, index_entryp->seg_idxs[1]);
  head = QO_SEG_HEAD (seg);
  spec = head->entity_spec;

  arg->info.name.spec_id = spec->info.spec.id;
  arg->info.name.meta_class = PT_NORMAL;
  arg->info.name.resolved = spec->info.spec.range_var->info.name.original;

  arg->data_type = pt_domain_to_data_type (parser, index_entryp->constraints->attributes[0]->domain);
  if (arg->data_type == NULL)
    {
      goto exit_error;
    }

  arg->type_enum = arg->data_type->type_enum;

  db_make_null (&temp_null);
  val = pt_dbval_to_value (parser, &temp_null);
  if (val == NULL)
    {
      goto exit_error;
    }

  expr = pt_expression_2 (parser, PT_EQ, arg, val);
  if (expr == NULL)
    {
      goto exit_error;
    }

  term_exprs[0] = expr;

  return NO_ERROR;

exit_error:
  if (arg)
    {
      parser_free_tree (parser, arg);
    }
  if (val)
    {
      parser_free_tree (parser, val);
    }
  if (expr)
    {
      parser_free_tree (parser, expr);
    }

  return ER_FAILED;
}

/*
 * pt_fix_first_term_func_index_for_iss () - allocates needed first term for
 *              the index skip scan optimization (ISS) when a
 *                              function index is used
 *   return:
 *   parser(in):
 *   index_entryp(in):
 *   term_exprs(in):
 */
static int
pt_fix_first_term_func_index_for_iss (PARSER_CONTEXT * parser, QO_INDEX_ENTRY * index_entryp, PT_NODE ** term_exprs)
{
  int error = NO_ERROR;
  int query_str_len = 0;
  char *query_str = NULL;
  SM_FUNCTION_INFO *func_index = NULL;
  PT_NODE **stmt = NULL;
  PT_NODE *expr = NULL;
  PT_NODE *val = NULL;
  PT_NODE *new_term = NULL;
  DB_VALUE temp_null;
  PT_NODE *spec = NULL;
  QO_SEGMENT *seg = NULL;
  QO_NODE *head = NULL;
  char *class_name = NULL;
  SEMANTIC_CHK_INFO sc_info = { NULL, NULL, 0, 0, 0, false, false };

  assert (index_entryp->constraints->func_index_info);
  func_index = index_entryp->constraints->func_index_info;

  seg = QO_ENV_SEG (index_entryp->terms.env, index_entryp->seg_idxs[1]);
  head = QO_SEG_HEAD (seg);
  spec = head->entity_spec;
  class_name = (char *) spec->info.spec.entity_name->info.name.original;

  query_str_len = (int) strlen (func_index->expr_str) + (int) strlen (class_name) + 7 /* strlen("SELECT ") */  +
    6 /* strlen(" FROM ") */  +
    2 /* [] */  +
    1 /* terminating null */ ;
  query_str = (char *) malloc (query_str_len);
  if (query_str == NULL)
    {
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }

  snprintf (query_str, query_str_len, "SELECT %s FROM [%s]", func_index->expr_str, class_name);
  stmt = parser_parse_string_use_sys_charset (parser, query_str);
  if (stmt == NULL || *stmt == NULL || pt_has_error (parser))
    {
      error = ER_FAILED;
      goto error;
    }
  *stmt = pt_resolve_names (parser, *stmt, &sc_info);
  if (*stmt != NULL && !pt_has_error (parser))
    {
      *stmt = pt_semantic_type (parser, *stmt, &sc_info);
    }
  else
    {
      error = ER_FAILED;
      goto error;
    }
  expr = (*stmt)->info.query.q.select.list;

  db_make_null (&temp_null);
  val = pt_dbval_to_value (parser, &temp_null);
  if (val == NULL)
    {
      error = ER_FAILED;
      goto error;
    }

  new_term = pt_expression_2 (parser, PT_EQ, expr, val);
  if (expr == NULL)
    {
      error = ER_FAILED;
      goto error;
    }

  term_exprs[0] = new_term;
  free_and_init (query_str);

  return NO_ERROR;

error:
  if (query_str)
    {
      free_and_init (query_str);
    }
  if (expr)
    {
      parser_free_tree (parser, expr);
    }
  if (val)
    {
      parser_free_tree (parser, val);
    }
  if (new_term)
    {
      parser_free_tree (parser, new_term);
    }
  return error;
}

/*
 * pt_to_index_info () - Create an INDX_INFO structure for communication
 *  to a class access spec for eventual incorporation into an index scan
 *   return:
 *   parser(in):
 *   class(in):
 *   where_pred(in):
 *   plan(in):
 *   qo_index_infop(in):
 */
static INDX_INFO *
pt_to_index_info (PARSER_CONTEXT * parser, DB_OBJECT * class_, PRED_EXPR * where_pred, QO_PLAN * plan,
          QO_XASL_INDEX_INFO * qo_index_infop)
{
  int nterms;
  int rangelist_idx = -1;
  PT_NODE **term_exprs;
  PT_NODE *pt_expr;
  PT_OP_TYPE op_type = PT_LAST_OPCODE;
  INDX_INFO *indx_infop;
  QO_NODE_INDEX_ENTRY *ni_entryp;
  QO_INDEX_ENTRY *index_entryp;
#if !defined(NDEBUG)
  QO_INDEX_ENTRY *head_idxp;
#endif
  KEY_INFO *key_infop;
  int rc;
  int i;
  bool is_prefix_index;
  SM_FUNCTION_INFO *fi_info = NULL;

  assert (parser != NULL);
  assert (class_ != NULL);
  assert (plan != NULL);
  assert (qo_index_infop->ni_entry != NULL && qo_index_infop->ni_entry->head != NULL);

  if (!qo_is_interesting_order_scan (plan))
    {
      assert (false);
      PT_INTERNAL_ERROR (parser, "index plan generation - invalid plan");
      return NULL;
    }

  ni_entryp = qo_index_infop->ni_entry;

  for (i = 0, index_entryp = ni_entryp->head; i < ni_entryp->n; i++, index_entryp = index_entryp->next)
    {
      if (class_ == index_entryp->class_->mop)
    {
      break;        /* found */
    }
    }
  assert (index_entryp != NULL);

#if !defined(NDEBUG)
  head_idxp = ni_entryp->head;

  if (index_entryp != head_idxp)
    {
      assert (qo_is_prefix_index (index_entryp) == qo_is_prefix_index (head_idxp));
      assert (index_entryp->is_iss_candidate == head_idxp->is_iss_candidate);
      assert (index_entryp->cover_segments == head_idxp->cover_segments);
      assert (index_entryp->use_descending == head_idxp->use_descending);
      assert (index_entryp->orderby_skip == head_idxp->orderby_skip);
      assert (index_entryp->groupby_skip == head_idxp->groupby_skip);

      assert (QO_ENTRY_MULTI_COL (index_entryp) == QO_ENTRY_MULTI_COL (head_idxp));

      assert (index_entryp->ils_prefix_len == head_idxp->ils_prefix_len);
      assert (index_entryp->key_limit == head_idxp->key_limit);

      assert ((index_entryp->constraints->filter_predicate == NULL && head_idxp->constraints->filter_predicate == NULL)
          || (index_entryp->constraints->filter_predicate != NULL
          && head_idxp->constraints->filter_predicate != NULL));
      assert ((index_entryp->constraints->func_index_info == NULL && head_idxp->constraints->func_index_info == NULL)
          || (index_entryp->constraints->func_index_info != NULL
          && head_idxp->constraints->func_index_info != NULL));
    }
#endif

  /* get array of term expressions and number of them which are associated with this index */
  nterms = qo_xasl_get_num_terms (qo_index_infop);
  term_exprs = qo_xasl_get_terms (qo_index_infop);

  is_prefix_index = qo_is_prefix_index (index_entryp);

  if (class_ == NULL || nterms < 0 || index_entryp == NULL)
    {
      PT_INTERNAL_ERROR (parser, "index plan generation - invalid arg");
      return NULL;
    }

  /* fabricate the first term_expr, to complete the proper range search expression */
  if (qo_is_index_iss_scan (plan))
    {
      assert (index_entryp->is_iss_candidate);

      rc = pt_fix_first_term_expr_for_iss (parser, index_entryp, term_exprs);
      if (rc != NO_ERROR)
    {
      PT_INTERNAL_ERROR (parser, "index plan generation - invalid expr for iss");
      return NULL;
    }
    }

  if (nterms > 0)
    {
      int start_column = qo_is_index_iss_scan (plan) ? 1 : 0;
      rangelist_idx = -1;   /* init */
      for (i = start_column; i < nterms; i++)
    {
      pt_expr = term_exprs[i];
      assert (pt_expr != NULL);
      if (pt_expr->info.expr.op == PT_RANGE)
        {
          assert (pt_expr->info.expr.arg2 != NULL);

          if (pt_expr->info.expr.arg2)
        {
          PT_NODE *between_and;

          between_and = pt_expr->info.expr.arg2;
          if (between_and->or_next)
            {
              /* is RANGE (r1, r2, ...) */
              rangelist_idx = i;
              break;
            }
        }
        }
    }

      if (rangelist_idx == -1)
    {
      /* The last term expression in the array(that is, [nterms - 1]) is interesting because the multi-column index
       * scan depends on it. For example: a = ? AND b = ? AND c = ? a = ? AND b = ? AND c RANGE (r1) a = ? AND b =
       * ? AND c RANGE (r1, r2, ...) */
      rangelist_idx = nterms - 1;
      op_type = term_exprs[rangelist_idx]->info.expr.op;
    }
      else
    {
      /* Have non-last EQUAL range term and is only one. For example: a = ? AND b RANGE (r1=, r2=, ...) AND c = ? a
       * = ? AND b RANGE (r1=, r2=, ...) AND c RANGE (r1)
       *
       * but, the following is not permitted. a = ? AND b RANGE (r1=, r2=, ...) AND c RANGE (r1, r2, ...) */
      op_type = PT_RANGE;
    }
    }

  /* make INDX_INFO structure and fill it up using information in QO_XASL_INDEX_INFO structure */
  regu_alloc (indx_infop);
  if (indx_infop == NULL)
    {
      PT_INTERNAL_ERROR (parser, "index plan generation - memory alloc");
      return NULL;
    }

  /* BTID */
  BTID_COPY (&indx_infop->btid, &index_entryp->constraints->index_btid);

  /* check for covered index scan */
  indx_infop->coverage = 0; /* init */
  if (qo_is_index_covering_scan (plan))
    {
      assert (index_entryp->cover_segments == true);
      assert (where_pred == NULL);  /* no data-filter */
      assert (is_prefix_index == false);

      if (index_entryp->cover_segments == true && where_pred == NULL && is_prefix_index == false)
    {
      indx_infop->coverage = 1;
    }
    }

  if (indx_infop->coverage)
    {
      COLL_OPT collation_opt;

      if (qo_check_type_index_covering (index_entryp) == false)
    {
      indx_infop->coverage = false;
    }

      if (indx_infop->coverage)
    {
      qo_check_coll_optimization (index_entryp, &collation_opt);
      indx_infop->coverage = collation_opt.allow_index_opt;
      /* alloc list file id for covering index */
      regu_alloc (indx_infop->cov_list_id);
      if (indx_infop->cov_list_id == NULL)
        {
          PT_INTERNAL_ERROR (parser, "index plan generation - memory alloc");
          return NULL;
        }
    }
    }

  indx_infop->class_oid = class_->oid_info.oid;
  indx_infop->use_desc_index = index_entryp->use_descending;
  indx_infop->orderby_skip = index_entryp->orderby_skip;
  indx_infop->groupby_skip = index_entryp->groupby_skip;

  /* 0 for now, see gen optimized plan for its computation */
  indx_infop->orderby_desc = 0;
  indx_infop->groupby_desc = 0;

  indx_infop->use_iss = 0;  /* init */
  if (qo_is_index_iss_scan (plan))
    {
      assert (QO_ENTRY_MULTI_COL (index_entryp));
      assert (!qo_is_index_loose_scan (plan));
      assert (!qo_plan_multi_range_opt (plan));
      assert (!qo_is_filter_index (index_entryp));

      indx_infop->use_iss = 1;
    }

  /* check for loose index scan */
  indx_infop->ils_prefix_len = 0;   /* init */
  if (qo_is_index_loose_scan (plan))
    {
      assert (QO_ENTRY_MULTI_COL (index_entryp));
      assert (qo_is_index_covering_scan (plan));
      assert (is_prefix_index == false);
      assert (!qo_is_index_iss_scan (plan));
      assert (!qo_plan_multi_range_opt (plan));

      assert (where_pred == NULL);  /* no data-filter */

      indx_infop->ils_prefix_len = index_entryp->ils_prefix_len;
      assert (indx_infop->ils_prefix_len > 0);
    }

  fi_info = index_entryp->constraints->func_index_info;
  if (fi_info)
    {
      indx_infop->func_idx_col_id = fi_info->col_id;
      assert (indx_infop->func_idx_col_id != -1);

      rc =
    pt_create_iss_range (indx_infop,
                 tp_domain_resolve (fi_info->fi_domain->type->id, class_, fi_info->fi_domain->precision,
                        fi_info->fi_domain->scale, NULL, fi_info->fi_domain->collation_id));
    }
  else
    {
      indx_infop->func_idx_col_id = -1;

      rc = pt_create_iss_range (indx_infop, index_entryp->constraints->attributes[0]->domain);
    }
  if (rc != NO_ERROR)
    {
      PT_INTERNAL_ERROR (parser, "index plan generation - create iss range fail");
      return NULL;
    }

  /* key limits */
  key_infop = &indx_infop->key_info;
  if (pt_to_key_limit (parser, index_entryp->key_limit, NULL, key_infop, false) != NO_ERROR)
    {
      PT_INTERNAL_ERROR (parser, "index plan generation - invalid key limit");
      return NULL;
    }

  if (nterms == 0)
    {
      key_infop->key_cnt = 1;
      key_infop->is_constant = false;
      regu_array_alloc (&key_infop->key_ranges, 1);
      if (key_infop->key_ranges == NULL)
    {
      PT_INTERNAL_ERROR (parser, "index plan generation - memory alloc");
      return NULL;
    }

      key_infop->key_ranges[0].key1 = NULL;
      key_infop->key_ranges[0].key2 = NULL;
      key_infop->key_ranges[0].range = INF_INF;

      indx_infop->range_type = R_RANGE;

      goto end;
    }

  /* scan range spec and index key information */
  switch (op_type)
    {
    case PT_EQ:
      rc = pt_to_single_key (parser, term_exprs, nterms, QO_ENTRY_MULTI_COL (index_entryp), key_infop,
                 qo_index_infop->multi_col_pos);
      indx_infop->range_type = R_KEY;
      break;
    case PT_GT:
    case PT_GE:
    case PT_LT:
    case PT_LE:
    case PT_BETWEEN:
      rc = pt_to_range_key (parser, term_exprs, nterms, QO_ENTRY_MULTI_COL (index_entryp), key_infop);
      indx_infop->range_type = R_RANGE;
      break;
    case PT_IS_IN:
    case PT_EQ_SOME:
      rc = pt_to_list_key (parser, term_exprs, nterms, QO_ENTRY_MULTI_COL (index_entryp), key_infop);
      indx_infop->range_type = R_KEYLIST;
      break;
    case PT_RANGE:
      rc = pt_to_rangelist_key (parser, term_exprs, nterms, QO_ENTRY_MULTI_COL (index_entryp), key_infop, rangelist_idx,
                qo_index_infop->multi_col_pos);
      for (i = 0; i < key_infop->key_cnt; i++)
    {
      if (key_infop->key_ranges[i].range != EQ_NA)
        {
          break;
        }
    }
      if (i < key_infop->key_cnt)
    {
      indx_infop->range_type = R_RANGELIST;
    }
      else
    {
      indx_infop->range_type = R_KEYLIST;   /* attr IN (?, ?) */
    }
      break;
    default:
      /* the other operators are not applicable to index scan */
      rc = -1;
    }

  if (rc < 0)
    {
      PT_INTERNAL_ERROR (parser, "index plan generation - invalid key value");
      return NULL;
    }

  if (is_prefix_index)
    {
      for (i = 0; i < key_infop->key_cnt; i++)
    {
      if (key_infop->key_ranges[i].range == GT_INF)
        {
          key_infop->key_ranges[i].range = GE_INF;
        }
      if (key_infop->key_ranges[i].range == GT_LT)
        {
          key_infop->key_ranges[i].range = GE_LT;
        }
      if (key_infop->key_ranges[i].range == GT_LE)
        {
          key_infop->key_ranges[i].range = GE_LE;
        }
    }
    }

end:
  if (key_infop->key_cnt > 0)
    {
      regu_array_alloc (&key_infop->key_vals, key_infop->key_cnt);
      if (key_infop->key_vals == NULL)
    {
      PT_INTERNAL_ERROR (parser, "index plan generation - memory alloc");
      return NULL;
    }
    }
  return indx_infop;
}

/*
 * pt_get_mvcc_reev_range_data () - creates predicates for range filter
 *   return:
 *   table_info(in):
 *   where_key_part(in):
 *   index_pred(in):
 *   where_range(in/out):
 *   regu_attributes_range(in/out):
 *   cache_range(in/out):
 */
int
pt_get_mvcc_reev_range_data (PARSER_CONTEXT * parser, TABLE_INFO * table_info, PT_NODE * where_key_part,
                 QO_XASL_INDEX_INFO * index_pred, PRED_EXPR ** where_range,
                 REGU_VARIABLE_LIST * regu_attributes_range, HEAP_CACHE_ATTRINFO ** cache_range)
{
  int idx, *range_offsets = NULL, *range_rest_offsets = NULL;
  PT_NODE *where_range_part = NULL;
  PT_NODE *range_attrs = NULL, *range_rest_attrs = NULL;

  if (parser->symbols == NULL)
    {
      return ER_FAILED;
    }
  if (where_key_part != NULL)
    {
      PT_NODE *diff1 = NULL, *diff2 = NULL;

      for (idx = 0; idx < index_pred->nterms; idx++)
    {
      diff1 = parser_get_tree_list_diff (parser, index_pred->term_exprs[idx], where_key_part);
      if (diff1 != NULL)
        {
          diff2 = parser_get_tree_list_diff (parser, diff1, where_range_part);
          parser_free_tree (parser, diff1);
          where_range_part = parser_append_node (diff2, where_range_part);
        }
    }
    }
  else
    {
      for (idx = 0; idx < index_pred->nterms; idx++)
    {
      where_range_part =
        parser_append_node (parser_copy_tree (parser, index_pred->term_exprs[idx]), where_range_part);
    }
    }
  if (pt_split_attrs (parser, table_info, where_range_part, &range_attrs, &range_rest_attrs, NULL, &range_offsets,
              &range_rest_offsets, NULL) != NO_ERROR)
    {
      parser_free_tree (parser, where_range_part);
      return ER_FAILED;
    }

  regu_alloc (*cache_range);
  parser->symbols->cache_attrinfo = *cache_range;

  *where_range = pt_to_pred_expr (parser, where_range_part);

  *regu_attributes_range =
    pt_to_regu_variable_list (parser, range_attrs, UNBOX_AS_VALUE, table_info->value_list, range_offsets);

  parser_free_tree (parser, where_range_part);
  parser_free_tree (parser, range_attrs);
  free_and_init (range_offsets);
  free_and_init (range_rest_offsets);

  return NO_ERROR;
}

/*
 * pt_to_class_spec_list () - Convert a PT_NODE flat class list to
 *     an ACCESS_SPEC_LIST list of representing the classes to be selected from
 *   return:
 *   parser(in):
 *   spec(in):
 *   where_key_part(in):
 *   where_part(in):
 *   plan(in):
 *   index_pred(in):
 */
static ACCESS_SPEC_TYPE *
pt_to_class_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * where_key_part, PT_NODE * where_part,
               QO_PLAN * plan, QO_XASL_INDEX_INFO * index_pred)
{
  SYMBOL_INFO *symbols = NULL;
  ACCESS_SPEC_TYPE *access = NULL;
  ACCESS_SPEC_TYPE *access_list = NULL;
  PT_NODE *flat;
  PT_NODE *class_;
  PRED_EXPR *where_key = NULL;
  REGU_VARIABLE_LIST regu_attributes_key;
  HEAP_CACHE_ATTRINFO *cache_key = NULL;
  PT_NODE *key_attrs = NULL;
  int *key_offsets = NULL;
  PRED_EXPR *where = NULL, *where_range = NULL;
  REGU_VARIABLE_LIST regu_attributes_pred, regu_attributes_rest;
  REGU_VARIABLE_LIST regu_attributes_reserved;
  REGU_VARIABLE_LIST regu_attributes_range = NULL;
  TABLE_INFO *table_info = NULL;
  INDX_INFO *index_info = NULL;
  HEAP_CACHE_ATTRINFO *cache_pred = NULL, *cache_rest = NULL;
  HEAP_CACHE_ATTRINFO *cache_range = NULL;
  PT_NODE *pred_attrs = NULL, *rest_attrs = NULL, *reserved_attrs = NULL;
  int *pred_offsets = NULL, *rest_offsets = NULL, *reserved_offsets = NULL;
  OUTPTR_LIST *output_val_list = NULL;
  REGU_VARIABLE_LIST regu_var_list = NULL;
  DB_VALUE **db_values_array_p = NULL;

  assert (parser != NULL);

  if (spec == NULL)
    {
      return NULL;
    }

  flat = spec->info.spec.flat_entity_list;
  if (flat == NULL)
    {
      return NULL;
    }

  symbols = parser->symbols;
  if (symbols == NULL)
    {
      return NULL;
    }

  table_info = pt_find_table_info (flat->info.name.spec_id, symbols->table_info);

  if (table_info)
    {
      for (class_ = flat; class_ != NULL; class_ = class_->next)
    {
      /* The scans have changed to grab the val list before predicate evaluation since evaluation now does
       * comparisons using DB_VALUES instead of disk rep.  Thus, the where predicate does NOT want to generate
       * TYPE_ATTR_ID regu variables, but rather TYPE_CONSTANT regu variables. This is driven off the
       * symbols->current class variable so we need to generate the where pred first. */

      if (index_pred == NULL)
        {
          /* Heap scan */
          TARGET_TYPE scan_type;
          ACCESS_METHOD access_method = ACCESS_METHOD_SEQUENTIAL;

          /* determine access_method */
          if (PT_IS_SPEC_FLAG_SET (spec, PT_SPEC_FLAG_RECORD_INFO_SCAN))
        {
          access_method = ACCESS_METHOD_SEQUENTIAL_RECORD_INFO;
        }
          else if (PT_IS_SPEC_FLAG_SET (spec, PT_SPEC_FLAG_SAMPLING_SCAN))
        {
          access_method = ACCESS_METHOD_SEQUENTIAL_SAMPLING_SCAN;
        }
          else if (PT_IS_SPEC_FLAG_SET (spec, PT_SPEC_FLAG_PAGE_INFO_SCAN))
        {
          access_method = ACCESS_METHOD_SEQUENTIAL_PAGE_SCAN;
        }

          /* for VALUES query, a new scan type is set */
          if (PT_IS_VALUE_QUERY (spec))
        {
          scan_type = TARGET_REGUVAL_LIST;
        }
          else if (spec->info.spec.meta_class == PT_META_CLASS)
        {
          scan_type = TARGET_CLASS_ATTR;
        }
          else
        {
          scan_type = TARGET_CLASS;
        }

          if (pt_split_attrs (parser, table_info, where_part, &pred_attrs, &rest_attrs, &reserved_attrs,
                  &pred_offsets, &rest_offsets, &reserved_offsets) != NO_ERROR)
        {
          return NULL;
        }

          if (access_method == ACCESS_METHOD_SEQUENTIAL_PAGE_SCAN)
        {
          cache_pred = NULL;
          cache_rest = NULL;

          db_values_array_p = pt_make_reserved_value_list (parser, RESERVED_NAME_PAGE_INFO);
        }
          else
        {
          regu_alloc (cache_pred);
          regu_alloc (cache_rest);
          if (access_method == ACCESS_METHOD_SEQUENTIAL_RECORD_INFO)
            {
              db_values_array_p = pt_make_reserved_value_list (parser, RESERVED_NAME_RECORD_INFO);
            }
        }

          symbols->current_class = (scan_type == TARGET_CLASS_ATTR) ? NULL : class_;
          symbols->cache_attrinfo = cache_pred;
          symbols->reserved_values = db_values_array_p;

          where = pt_to_pred_expr (parser, where_part);

          if (scan_type == TARGET_CLASS_ATTR)
        {
          symbols->current_class = class_;
        }

          regu_attributes_pred =
        pt_to_regu_variable_list (parser, pred_attrs, UNBOX_AS_VALUE, table_info->value_list, pred_offsets);

          symbols->cache_attrinfo = cache_rest;

          regu_attributes_rest =
        pt_to_regu_variable_list (parser, rest_attrs, UNBOX_AS_VALUE, table_info->value_list, rest_offsets);

          regu_attributes_reserved =
        pt_to_regu_variable_list (parser, reserved_attrs, UNBOX_AS_VALUE, table_info->value_list,
                      reserved_offsets);

          output_val_list = NULL;
          regu_var_list = NULL;

          parser_free_tree (parser, pred_attrs);
          parser_free_tree (parser, rest_attrs);
          parser_free_tree (parser, reserved_attrs);
          if (pred_offsets != NULL)
        {
          free_and_init (pred_offsets);
        }
          if (rest_offsets != NULL)
        {
          free_and_init (rest_offsets);
        }
          if (reserved_offsets != NULL)
        {
          free_and_init (reserved_offsets);
        }

          access =
        pt_make_class_access_spec (parser, flat, class_->info.name.db_object, scan_type, access_method, NULL,
                       NULL, where, NULL, NULL, regu_attributes_pred, regu_attributes_rest, NULL,
                       output_val_list, regu_var_list, NULL, cache_pred, cache_rest,
                       NULL, NO_SCHEMA, db_values_array_p, regu_attributes_reserved);
          if (access_method == ACCESS_METHOD_SEQUENTIAL
          && PT_IS_SPEC_FLAG_SET (spec, PT_SPEC_FLAG_NO_PARALLEL_HEAP_SCAN))
        {
          ACCESS_SPEC_SET_FLAG (access, ACCESS_SPEC_FLAG_NO_PARALLEL_HEAP_SCAN);
        }

          if (PT_IS_SPEC_FLAG_SET (spec, PT_SPEC_FLAG_PARALLEL_THREAD))
        {
          ACCESS_SPEC_SET_FLAG (access, ACCESS_SPEC_FLAG_NUM_PARALLEL_THREADS);
          access->num_parallel_threads = spec->info.spec.num_parallel_threads;
        }
          else
        {
          assert (access->num_parallel_threads == -1 /* auto-compute */ );
        }

        }
      else if (PT_SPEC_SPECIAL_INDEX_SCAN (spec))
        {
          /* Index scan for key info */
          PT_RESERVED_NAME_TYPE reserved_type = RESERVED_NAME_INVALID;
          ACCESS_METHOD access_method = ACCESS_METHOD_SEQUENTIAL;

          if (pt_split_attrs (parser, table_info, where_part, &pred_attrs, &rest_attrs, &reserved_attrs,
                  &pred_offsets, &rest_offsets, &reserved_offsets) != NO_ERROR)
        {
          return NULL;
        }
          /* pred_attrs and rest_attrs should have only class attributes. key info scan only allows selecting
           * reserved key info names. */
          assert (pred_attrs == NULL && rest_attrs == NULL && reserved_attrs != NULL);

          if (PT_IS_SPEC_FLAG_SET (spec, PT_SPEC_FLAG_KEY_INFO_SCAN))
        {
          reserved_type = RESERVED_NAME_KEY_INFO;
          access_method = ACCESS_METHOD_INDEX_KEY_INFO;
        }
          else if (PT_IS_SPEC_FLAG_SET (spec, PT_SPEC_FLAG_BTREE_NODE_INFO_SCAN))
        {
          reserved_type = RESERVED_NAME_BTREE_NODE_INFO;
          access_method = ACCESS_METHOD_INDEX_NODE_INFO;
        }
          else
        {
          /* Should never happen */
          assert (0);
        }
          db_values_array_p = pt_make_reserved_value_list (parser, reserved_type);

          symbols->current_class = class_;
          symbols->reserved_values = db_values_array_p;

          where = pt_to_pred_expr (parser, where_part);

          output_val_list = pt_make_outlist_from_vallist (parser, table_info->value_list);

          regu_attributes_pred = NULL;
          regu_attributes_rest = NULL;
          regu_attributes_reserved =
        pt_to_regu_variable_list (parser, reserved_attrs, UNBOX_AS_VALUE, table_info->value_list,
                      reserved_offsets);

          parser_free_tree (parser, pred_attrs);
          parser_free_tree (parser, rest_attrs);
          parser_free_tree (parser, reserved_attrs);
          if (pred_offsets != NULL)
        {
          free_and_init (pred_offsets);
        }
          if (rest_offsets != NULL)
        {
          free_and_init (rest_offsets);
        }
          if (reserved_offsets != NULL)
        {
          free_and_init (reserved_offsets);
        }

          index_info = pt_to_index_info (parser, class_->info.name.db_object, where, plan, index_pred);
          access =
        pt_make_class_access_spec (parser, flat, class_->info.name.db_object, TARGET_CLASS, access_method,
                       index_info, NULL, where, NULL, NULL, NULL, NULL, NULL, output_val_list, NULL,
                       NULL, NULL, NULL, NULL, NO_SCHEMA, db_values_array_p,
                       regu_attributes_reserved);
        }
      else
        {
          /* Index scan */
          /* for index with prefix length */
          PT_NODE *where_part_save = NULL, *where_key_part_save = NULL;
          PT_NODE *ipl_where_part = NULL;

          if (index_pred->ni_entry && index_pred->ni_entry->head && qo_is_prefix_index (index_pred->ni_entry->head))
        {
          if (index_pred->nterms > 0 || where_key_part)
            {
              ipl_where_part =
            pt_make_prefix_index_data_filter (parser, where_key_part, where_part, index_pred);
            }

          if (ipl_where_part)
            {
              where_part_save = where_part;
              where_part = ipl_where_part;
              where_key_part_save = where_key_part;
              where_key_part = NULL;
            }
        }

          if (!pt_to_index_attrs (parser, table_info, index_pred, where_key_part, &key_attrs, &key_offsets))
        {
          if (ipl_where_part)
            {
              parser_free_tree (parser, where_part);
              where_part = where_part_save;
              where_key_part = where_key_part_save;
            }
          return NULL;
        }
          if (pt_split_attrs (parser, table_info, where_part, &pred_attrs, &rest_attrs, NULL, &pred_offsets,
                  &rest_offsets, NULL) != NO_ERROR)
        {
          if (ipl_where_part)
            {
              parser_free_tree (parser, where_part);
              where_part = where_part_save;
              where_key_part = where_key_part_save;
            }
          parser_free_tree (parser, key_attrs);
          if (key_offsets != NULL)
            {
              free_and_init (key_offsets);
            }
          return NULL;
        }

          symbols->current_class = class_;

          if (pt_get_mvcc_reev_range_data (parser, table_info, where_key_part, index_pred, &where_range,
                           &regu_attributes_range, &cache_range) != NO_ERROR)
        {
          parser_free_tree (parser, key_attrs);
          if (key_offsets != NULL)
            {
              free_and_init (key_offsets);
            }
          parser_free_tree (parser, pred_attrs);
          if (pred_offsets != NULL)
            {
              free_and_init (pred_offsets);
            }
          parser_free_tree (parser, rest_attrs);
          if (rest_offsets != NULL)
            {
              free_and_init (rest_offsets);
            }
          return NULL;
        }

          regu_alloc (cache_key);
          regu_alloc (cache_pred);
          regu_alloc (cache_rest);

          symbols->cache_attrinfo = cache_key;

          where_key = pt_to_pred_expr (parser, where_key_part);

          regu_attributes_key =
        pt_to_regu_variable_list (parser, key_attrs, UNBOX_AS_VALUE, table_info->value_list, key_offsets);

          symbols->cache_attrinfo = cache_pred;

          where = pt_to_pred_expr (parser, where_part);

          regu_attributes_pred =
        pt_to_regu_variable_list (parser, pred_attrs, UNBOX_AS_VALUE, table_info->value_list, pred_offsets);

          symbols->cache_attrinfo = cache_rest;

          regu_attributes_rest =
        pt_to_regu_variable_list (parser, rest_attrs, UNBOX_AS_VALUE, table_info->value_list, rest_offsets);

          output_val_list = pt_make_outlist_from_vallist (parser, table_info->value_list);

          regu_var_list =
        pt_to_position_regu_variable_list (parser, rest_attrs, table_info->value_list, rest_offsets);

          parser_free_tree (parser, key_attrs);
          parser_free_tree (parser, pred_attrs);
          parser_free_tree (parser, rest_attrs);
          if (key_offsets != NULL)
        {
          free_and_init (key_offsets);
        }
          if (pred_offsets != NULL)
        {
          free_and_init (pred_offsets);
        }
          if (rest_offsets != NULL)
        {
          free_and_init (rest_offsets);
        }

          /*
           * pt_make_class_spec() will return NULL if passed a
           * NULL INDX_INFO *, so there isn't any need to check
           * return values here.
           */
          index_info = pt_to_index_info (parser, class_->info.name.db_object, where, plan, index_pred);

          if (pt_has_error (parser))
        {
          return NULL;
        }

          assert (index_info != NULL);
          access =
        pt_make_class_access_spec (parser, flat, class_->info.name.db_object, TARGET_CLASS, ACCESS_METHOD_INDEX,
                       index_info, where_key, where, where_range, regu_attributes_key,
                       regu_attributes_pred, regu_attributes_rest, regu_attributes_range,
                       output_val_list, regu_var_list, cache_key, cache_pred, cache_rest,
                       cache_range, NO_SCHEMA, NULL, NULL);

          if (ipl_where_part)
        {
          parser_free_tree (parser, where_part);
          where_part = where_part_save;
          where_key_part = where_key_part_save;
        }
        }

      if (access == NULL
          || (regu_attributes_pred == NULL && regu_attributes_rest == NULL && table_info->attribute_list != NULL
          && access->access == ACCESS_METHOD_SEQUENTIAL) || pt_has_error (parser))
        {
          /* an error condition */
          access = NULL;
        }

      if (access)
        {
          if (spec->info.spec.flag & PT_SPEC_FLAG_FOR_UPDATE_CLAUSE)
        {
          ACCESS_SPEC_SET_FLAG (access, ACCESS_SPEC_FLAG_FOR_UPDATE);
        }

          access->next = access_list;
          access_list = access;
        }
      else
        {
          /* an error condition */
          access_list = NULL;
          break;
        }
    }

      symbols->current_class = NULL;
      symbols->cache_attrinfo = NULL;

    }

  return access_list;
}

/*
 * pt_to_showstmt_spec_list () - Convert a QUERY PT_NODE
 *  an showstmt query
 *   return:
 *   parser(in):
 *   spec(in):
 *   where_part(in):
 */
static ACCESS_SPEC_TYPE *
pt_to_showstmt_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * where_part)
{
  PT_NODE *saved_current_class;
  PT_NODE *derived_table;
  SHOWSTMT_TYPE show_type;
  PT_NODE *show_args;
  REGU_VARIABLE_LIST arg_list;
  ACCESS_SPEC_TYPE *access;
  PRED_EXPR *where = NULL;

  if (spec->info.spec.derived_table_type != PT_IS_SHOWSTMT || (derived_table = spec->info.spec.derived_table) == NULL
      || derived_table->node_type != PT_SHOWSTMT)
    {
      return NULL;
    }

  saved_current_class = parser->symbols->current_class;
  parser->symbols->current_class = NULL;
  where = pt_to_pred_expr (parser, where_part);
  parser->symbols->current_class = saved_current_class;
  if (where_part != NULL && where == NULL)
    {
      return NULL;
    }

  show_type = derived_table->info.showstmt.show_type;
  show_args = derived_table->info.showstmt.show_args;
  arg_list = pt_to_regu_variable_list (parser, show_args, UNBOX_AS_VALUE, NULL, NULL);
  if (show_args != NULL && arg_list == NULL)
    {
      return NULL;
    }

  access = pt_make_showstmt_access_spec (where, show_type, arg_list);

  return access;
}

/*
 * pt_to_subquery_table_spec_list () - Convert a QUERY PT_NODE
 *  an ACCESS_SPEC_LIST list for its list file
 *   return:
 *   parser(in):
 *   spec(in):
 *   subquery(in):
 *   where_part(in):
 */
static ACCESS_SPEC_TYPE *
pt_to_subquery_table_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * subquery, PT_NODE * where_part,
                PT_NODE * where_hash_part)
{
  XASL_NODE *subquery_proc;
  PT_NODE *saved_current_class;
  REGU_VARIABLE_LIST regu_attributes_pred, regu_attributes_rest, regu_attributes_build, regu_attributes_probe;
  ACCESS_SPEC_TYPE *access;
  PRED_EXPR *where = NULL;
  TABLE_INFO *tbl_info;
  PT_NODE *pred_attrs = NULL, *rest_attrs = NULL, *build_attrs = NULL, *probe_attrs = NULL;
  int *pred_offsets = NULL, *rest_offsets = NULL;

  subquery_proc = (XASL_NODE *) subquery->info.query.xasl;

  tbl_info = pt_find_table_info (spec->info.spec.id, parser->symbols->table_info);

  if (pt_split_attrs (parser, tbl_info, where_part, &pred_attrs, &rest_attrs, NULL, &pred_offsets, &rest_offsets, NULL)
      != NO_ERROR)
    {
      return NULL;
    }

  /* This generates a list of TYPE_POSITION regu_variables There information is stored in a QFILE_TUPLE_VALUE_POSITION,
   * which describes a type and index into a list file. */
  regu_attributes_pred = pt_to_position_regu_variable_list (parser, pred_attrs, tbl_info->value_list, pred_offsets);
  regu_attributes_rest = pt_to_position_regu_variable_list (parser, rest_attrs, tbl_info->value_list, rest_offsets);

  parser_free_tree (parser, pred_attrs);
  parser_free_tree (parser, rest_attrs);
  free_and_init (pred_offsets);
  free_and_init (rest_offsets);

  if (pt_split_hash_attrs (parser, tbl_info, where_hash_part, &build_attrs, &probe_attrs) != NO_ERROR)
    {
      return NULL;
    }
  regu_attributes_build = pt_to_regu_variable_list (parser, build_attrs, UNBOX_AS_VALUE, tbl_info->value_list, NULL);
  regu_attributes_probe = pt_to_regu_variable_list (parser, probe_attrs, UNBOX_AS_VALUE, tbl_info->value_list, NULL);

  parser_free_tree (parser, build_attrs);
  parser_free_tree (parser, probe_attrs);

  parser->symbols->listfile_unbox = UNBOX_AS_VALUE;
  parser->symbols->current_listfile = NULL;

  /* The where predicate is now evaluated after the val list has been fetched.  This means that we want to generate
   * "CONSTANT" regu variables instead of "POSITION" regu variables which would happen if
   * parser->symbols->current_listfile != NULL. pred should never user the current instance for fetches either, so we
   * turn off the current_class, if there is one. */
  saved_current_class = parser->symbols->current_class;
  parser->symbols->current_class = NULL;
  where = pt_to_pred_expr (parser, where_part);
  parser->symbols->current_class = saved_current_class;

  access =
    pt_make_list_access_spec (subquery_proc, ACCESS_METHOD_SEQUENTIAL, NULL, where, regu_attributes_pred,
                  regu_attributes_rest, regu_attributes_build, regu_attributes_probe);

  if (access && subquery_proc && (regu_attributes_pred || regu_attributes_rest || !spec->info.spec.as_attr_list))
    {
      return access;
    }

  return NULL;
}

/*
 * pt_to_set_expr_table_spec_list () - Convert a PT_NODE flat class list
 *  to an ACCESS_SPEC_LIST list of representing the classes
 *  to be selected from
 *   return:
 *   parser(in):
 *   spec(in):
 *   set_expr(in):
 *   where_part(in):
 */
static ACCESS_SPEC_TYPE *
pt_to_set_expr_table_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * set_expr, PT_NODE * where_part)
{
  REGU_VARIABLE_LIST regu_attributes;
  REGU_VARIABLE *regu_set_expr;
  PRED_EXPR *where = NULL;

  ACCESS_SPEC_TYPE *access;

  regu_set_expr = pt_to_regu_variable (parser, set_expr, UNBOX_AS_VALUE);

  /* This generates a list of TYPE_POSITION regu_variables There information is stored in a QFILE_TUPLE_VALUE_POSITION,
   * which describes a type and index into a list file. */
  regu_attributes = pt_to_position_regu_variable_list (parser, spec->info.spec.as_attr_list, NULL, NULL);

  where = pt_to_pred_expr (parser, where_part);

  access = pt_make_set_access_spec (regu_set_expr, ACCESS_METHOD_SEQUENTIAL, NULL, where, regu_attributes);

  if (access && regu_set_expr && (regu_attributes || !spec->info.spec.as_attr_list))
    {
      return access;
    }

  return NULL;
}

/*
 * pt_to_cselect_table_spec_list () - Convert a PT_NODE flat class list to
 *     an ACCESS_SPEC_LIST list of representing the classes to be selected from
 *   return:
 *   parser(in):
 *   spec(in):
 *   cselect(in):
 *   src_derived_tbl(in):
 */
static ACCESS_SPEC_TYPE *
pt_to_cselect_table_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * cselect, PT_NODE * src_derived_tbl)
{
  XASL_NODE *subquery_proc;
  REGU_VARIABLE_LIST regu_attributes;
  ACCESS_SPEC_TYPE *access;
  PL_SIGNATURE_ARRAY_TYPE *sig_array = NULL;
  PL_SIGNATURE_TYPE *sig_list = NULL;
  int idx = 0;

  /* every cselect must have a subquery for its source list file, this is pointed to by the methods of the cselect */
  if (!cselect || !(PT_IS_METHOD (cselect)) || !src_derived_tbl || !PT_SPEC_IS_DERIVED (src_derived_tbl))
    {
      return NULL;
    }

  subquery_proc = (XASL_NODE *) src_derived_tbl->info.spec.derived_table->info.query.xasl;

  regu_alloc (sig_array);
  if (sig_array == NULL)
    {
      return NULL;
    }
  new (sig_array) cubpl::pl_signature_array ();

  sig_array->num_sigs = pt_length_of_list (cselect);

  regu_array_alloc (&sig_list, sig_array->num_sigs);
  sig_array->sigs = sig_list;

  for (int i = 0; i < sig_array->num_sigs; i++)
    {
      new (&sig_array->sigs[i]) cubpl::pl_signature ();
    }

  for (PT_NODE * node = cselect; node != NULL; node = node->next)
    {
      int error = jsp_make_pl_signature (parser, node, src_derived_tbl->info.spec.as_attr_list, sig_array->sigs[idx]);
      if (error != NO_ERROR)
    {
      return NULL;
    }
      idx++;
    }

  /* This generates a list of TYPE_POSITION regu_variables There information is stored in a QFILE_TUPLE_VALUE_POSITION,
   * which describes a type and index into a list file. */
  regu_attributes = pt_to_position_regu_variable_list (parser, spec->info.spec.as_attr_list, NULL, NULL);

  access =
    pt_make_cselect_access_spec (subquery_proc, sig_array, ACCESS_METHOD_SEQUENTIAL, NULL, NULL, regu_attributes);

  if (subquery_proc && sig_array && (regu_attributes || !spec->info.spec.as_attr_list))
    {
      return access;
    }

  return NULL;
}

static ACCESS_SPEC_TYPE *
pt_to_json_table_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * json_table,
                PT_NODE * src_derived_tbl, PT_NODE * where_p)
{
  ACCESS_SPEC_TYPE *access;

  PRED_EXPR *where = pt_to_pred_expr (parser, where_p);

  TABLE_INFO *tbl_info = pt_find_table_info (spec->info.spec.id, parser->symbols->table_info);
  assert (tbl_info != NULL);

  REGU_VARIABLE *regu_var = pt_to_regu_variable (parser, json_table->info.json_table_info.expr, UNBOX_AS_VALUE);

  access = pt_make_json_table_access_spec (parser, regu_var, where, &json_table->info.json_table_info, tbl_info);

  return access;
}

static PT_NODE *
pt_host_vars_count (PARSER_CONTEXT * parser, PT_NODE * term_list, void *arg, int *continue_walk)
{
  int *count = (int *) arg;

  if (term_list->node_type == PT_HOST_VAR)
    {
      (*count)++;
    }

  *continue_walk = PT_CONTINUE_WALK;

  return term_list;
}

static PT_NODE *
pt_host_vars_index (PARSER_CONTEXT * parser, PT_NODE * term_list, void *arg, int *continue_walk)
{
  PT_HOST_VAR_IDX_INFO *host_vars = (PT_HOST_VAR_IDX_INFO *) arg;

  if (term_list->node_type == PT_HOST_VAR)
    {
      host_vars->index[host_vars->count++] = term_list->info.host_var.index;
    }

  *continue_walk = PT_CONTINUE_WALK;

  return term_list;
}

static ACCESS_SPEC_TYPE *
pt_to_dblink_table_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * dblink_table,
                  PT_NODE * src_derived_tbl, PT_NODE * where_p)
{
  ACCESS_SPEC_TYPE *access;
  PT_DBLINK_INFO *pdblink = &(dblink_table->info.dblink_table);
  char *sql;
  int count = 0;

  PRED_EXPR *where = pt_to_pred_expr (parser, where_p);

  TABLE_INFO *tbl_info = pt_find_table_info (spec->info.spec.id, parser->symbols->table_info);
  assert (tbl_info != NULL);

  REGU_VARIABLE *regu_var = pt_to_regu_variable (parser, pdblink->qstr, UNBOX_AS_VALUE);
  ACCESS_METHOD access_method = ACCESS_METHOD_SEQUENTIAL;

  PT_NODE *pred_attrs = NULL, *rest_attrs = NULL, *reserved_attrs = NULL;
  int *pred_offsets = NULL, *rest_offsets = NULL, *reserved_offsets = NULL;

  if (pt_split_attrs (parser, tbl_info, where_p, &pred_attrs, &rest_attrs, &reserved_attrs,
              &pred_offsets, &rest_offsets, &reserved_offsets) != NO_ERROR)
    {
      return NULL;
    }

  REGU_VARIABLE_LIST regu_attributes_pred;
  REGU_VARIABLE_LIST regu_attributes_rest;

  regu_attributes_rest =
    pt_to_regu_variable_list (parser, rest_attrs, UNBOX_AS_VALUE, tbl_info->value_list, rest_offsets);

  regu_attributes_pred =
    pt_to_regu_variable_list (parser, pred_attrs, UNBOX_AS_VALUE, tbl_info->value_list, pred_offsets);

  if (pdblink->rewritten)
    {
      sql = pt_append_string (parser, "/* DBLINK SELECT */ ", (char *) pdblink->rewritten->bytes);
    }
  else
    {
      sql = pt_append_string (parser, "/* DBLINK SELECT */ ", (char *) pdblink->qstr->info.value.data_value.str->bytes);
    }

  if (pdblink->pushed_pred)
    {
      parser_walk_tree (parser, pdblink->pushed_pred, pt_host_vars_count, &count, NULL, NULL);
    }

  pdblink->host_vars.count = 0;
  if (count > 0)
    {
      pdblink->host_vars.index = (int *) parser_alloc (parser, count * sizeof (int));
      parser_walk_tree (parser, pdblink->pushed_pred, pt_host_vars_index, &pdblink->host_vars, NULL, NULL);
    }

  access = pt_make_dblink_access_spec (access_method, where, regu_attributes_pred, regu_attributes_rest,
                       (char *) pdblink->url->info.value.data_value.str->bytes,
                       (char *) pdblink->user->info.value.data_value.str->bytes,
                       (char *) pdblink->pwd->info.value.data_value.str->bytes,
                       pdblink->host_vars.count, pdblink->host_vars.index, (char *) sql);

  return access;
}

/*
 * pt_to_cte_table_spec_list () - Convert a PT_NODE CTE to an ACCESS_SPEC_LIST of representations
                  of the classes to be selected from
 * return:
 * parser(in):
 * spec(in):
 * cte_def(in):
 * where_part(in):
 */
static ACCESS_SPEC_TYPE *
pt_to_cte_table_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * cte_def, PT_NODE * where_part)
{
  XASL_NODE *cte_proc;
  PT_NODE *saved_current_class;
  TABLE_INFO *tbl_info;
  REGU_VARIABLE_LIST regu_attributes_pred, regu_attributes_rest;
  ACCESS_SPEC_TYPE *access;
  PT_NODE *pred_attrs = NULL, *rest_attrs = NULL;
  int *pred_offsets = NULL, *rest_offsets = NULL;
  PRED_EXPR *where = NULL;

  if (spec == NULL || cte_def == NULL || spec->info.spec.cte_pointer == NULL)
    {
      return NULL;
    }

  if (cte_def->info.cte.xasl)
    {
      cte_proc = (XASL_NODE *) cte_def->info.cte.xasl;
    }
  else
    {
      /* The CTE xasl is null because the recursive part xasl has not been generated yet, but this is not a problem
       * because the recursive part should have access only to the non recursive part.
       * This may also happen with a CTE referenced by another one. If CTE1 is referenced by CTE2, the XASL of CTE1
       * is not completed when reaching this function from CTE2. CTE2 is reached following *next* link of CTE1 before
       * CTE1 post function of xasl generation is executed.
       */
      PT_NODE *non_recursive_part = cte_def->info.cte.non_recursive_part;

      if (non_recursive_part && non_recursive_part->info.query.xasl)
    {
      cte_proc = (XASL_NODE *) non_recursive_part->info.query.xasl;
    }
      else
    {
      assert (false);
      return NULL;
    }
    }

  tbl_info = pt_find_table_info (spec->info.spec.id, parser->symbols->table_info);

  if (pt_split_attrs (parser, tbl_info, where_part, &pred_attrs, &rest_attrs, NULL, &pred_offsets, &rest_offsets, NULL)
      != NO_ERROR)
    {
      return NULL;
    }

  /* This generates a list of TYPE_POSITION regu_variables
   * There information is stored in a QFILE_TUPLE_VALUE_POSITION, which
   * describes a type and index into a list file.
   */
  regu_attributes_pred = pt_to_position_regu_variable_list (parser, pred_attrs, tbl_info->value_list, pred_offsets);
  regu_attributes_rest = pt_to_position_regu_variable_list (parser, rest_attrs, tbl_info->value_list, rest_offsets);

  parser_free_tree (parser, pred_attrs);
  parser_free_tree (parser, rest_attrs);
  free_and_init (pred_offsets);
  free_and_init (rest_offsets);

  parser->symbols->listfile_unbox = UNBOX_AS_VALUE;
  parser->symbols->current_listfile = NULL;

  /* The where predicate is now evaluated after the val list has been fetched.
   * This means that we want to generate "CONSTANT" regu variables instead of "POSITION" regu variables which would
   * happen if parser->symbols->current_listfile != NULL.
   * pred should never use the current instance for fetches either, so we turn off the current_class, if there is one.
   */
  saved_current_class = parser->symbols->current_class;
  parser->symbols->current_class = NULL;
  where = pt_to_pred_expr (parser, where_part);
  parser->symbols->current_class = saved_current_class;

  access =
    pt_make_list_access_spec (cte_proc, ACCESS_METHOD_SEQUENTIAL, NULL, where, regu_attributes_pred,
                  regu_attributes_rest, NULL, NULL);

  if (access && cte_proc && (regu_attributes_pred || regu_attributes_rest || !spec->info.spec.as_attr_list))
    {
      return access;
    }

  return NULL;
}

/*
 * pt_to_spec_list () - Convert a PT_NODE spec to an ACCESS_SPEC_LIST list of
 *      representing the classes to be selected from
 *   return:
 *   parser(in):
 *   spec(in):
 *   where_key_part(in):
 *   where_part(in):
 *   plan(in):
 *   index_part(in):
 *   src_derived_tbl(in):
 */
ACCESS_SPEC_TYPE *
pt_to_spec_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * where_key_part, PT_NODE * where_part,
         QO_PLAN * plan, QO_XASL_INDEX_INFO * index_part, PT_NODE * src_derived_tbl, PT_NODE * where_hash_part)
{
  ACCESS_SPEC_TYPE *access = NULL;

  if (spec->info.spec.flat_entity_list != NULL && !PT_SPEC_IS_CTE (spec) && !PT_SPEC_IS_DERIVED (spec))
    {
      access = pt_to_class_spec_list (parser, spec, where_key_part, where_part, plan, index_part);
    }
  else if (PT_SPEC_IS_DERIVED (spec))
    {
      /* derived table index_part better be NULL here! */
      if (spec->info.spec.derived_table_type == PT_IS_SUBQUERY)
    {
      access =
        pt_to_subquery_table_spec_list (parser, spec, spec->info.spec.derived_table, where_part, where_hash_part);
    }
      else if (spec->info.spec.derived_table_type == PT_IS_SET_EXPR)
    {
      /* a set expression derived table */
      access = pt_to_set_expr_table_spec_list (parser, spec, spec->info.spec.derived_table, where_part);
    }
      else if (spec->info.spec.derived_table_type == PT_IS_SHOWSTMT)
    {
      access = pt_to_showstmt_spec_list (parser, spec, where_part);
    }
      else if (spec->info.spec.derived_table_type == PT_IS_CSELECT)
    {
      /* a CSELECT derived table */
      access = pt_to_cselect_table_spec_list (parser, spec, spec->info.spec.derived_table, src_derived_tbl);
    }
      else if (spec->info.spec.derived_table_type == PT_DERIVED_JSON_TABLE)
    {
      /* PT_JSON_DERIVED_TABLE derived table */
      access =
        pt_to_json_table_spec_list (parser, spec, spec->info.spec.derived_table, src_derived_tbl, where_part);
    }
      else if (spec->info.spec.derived_table_type == PT_DERIVED_DBLINK_TABLE)
    {
      /* PT_DERIVED_DBLINK_TABLE derived table */
      access =
        pt_to_dblink_table_spec_list (parser, spec, spec->info.spec.derived_table, src_derived_tbl, where_part);
    }
      else
    {
      // unrecognized derived table type
      assert (false);
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
      return NULL;
    }
    }
  else
    {
      /* there is a cte_pointer inside spec */
      assert (PT_SPEC_IS_CTE (spec));

      /* the subquery should be in non_recursive_part of the cte */
      access = pt_to_cte_table_spec_list (parser, spec, spec->info.spec.cte_pointer->info.pointer.node, where_part);
    }

  return access;
}

/*
 * pt_to_val_list () -
 *   return: val_list corresponding to the entity spec
 *   parser(in):
 *   id(in):
 */
VAL_LIST *
pt_to_val_list (PARSER_CONTEXT * parser, UINTPTR id)
{
  SYMBOL_INFO *symbols;
  VAL_LIST *val_list = NULL;
  TABLE_INFO *table_info;

  if (parser)
    {
      symbols = parser->symbols;
      table_info = pt_find_table_info (id, symbols->table_info);

      if (table_info)
    {
      val_list = table_info->value_list;
    }
    }

  return val_list;
}

/*
 * pt_find_xasl () - appends the from list to the end of the to list
 *   return:
 *   list(in):
 *   match(in):
 */
static XASL_NODE *
pt_find_xasl (XASL_NODE * list, XASL_NODE * match)
{
  XASL_NODE *xasl = list;

  while (xasl && xasl != match)
    {
      xasl = xasl->next;
    }

  return xasl;
}

/*
 * pt_append_xasl () - appends the from list to the end of the to list
 *   return:
 *   to(in):
 *   from_list(in):
 */
XASL_NODE *
pt_append_xasl (XASL_NODE * to, XASL_NODE * from_list)
{
  XASL_NODE *xasl = to;
  XASL_NODE *next;
  XASL_NODE *from = from_list;

  if (!xasl)
    {
      return from_list;
    }

  while (xasl->next)
    {
      xasl = xasl->next;
    }

  while (from)
    {
      next = from->next;

      if (pt_find_xasl (to, from))
    {
      /* already on list, do nothing necessarily, the rest of the nodes are on the list, since they are linked to
       * from. */
      from = NULL;
    }
      else
    {
      xasl->next = from;
      xasl = from;
      from->next = NULL;
      from = next;
    }
    }

  return to;
}


/*
 * pt_remove_xasl () - removes an xasl node from an xasl list
 *   return:
 *   xasl_list(in):
 *   remove(in):
 */
XASL_NODE *
pt_remove_xasl (XASL_NODE * xasl_list, XASL_NODE * remove)
{
  XASL_NODE *list = xasl_list;

  if (!list)
    {
      return list;
    }

  if (list == remove)
    {
      xasl_list = remove->next;
      remove->next = NULL;
    }
  else
    {
      while (list->next && list->next != remove)
    {
      list = list->next;
    }

      if (list->next == remove)
    {
      list->next = remove->next;
      remove->next = NULL;
    }
    }

  return xasl_list;
}

/*
 * pt_set_dptr () - If this xasl node should have a dptr list from
 *  "correlated == 1" queries, they will be set
 *   return:
 *   parser(in):
 *   node(in):
 *   xasl(in):
 *   id(in):
 */
void
pt_set_dptr (PARSER_CONTEXT * parser, PT_NODE * node, XASL_NODE * xasl, UINTPTR id)
{
  if (xasl)
    {
      xasl->dptr_list =
    pt_remove_xasl (pt_append_xasl (xasl->dptr_list, pt_to_corr_subquery_list (parser, node, id)), xasl);
    }
}

/*
 * pt_set_aptr () - If this xasl node should have an aptr list from
 *  "correlated > 1" queries, they will be set
 *   return:
 *   parser(in):
 *   select_node(in):
 *   xasl(in):
 */
static void
pt_set_aptr (PARSER_CONTEXT * parser, PT_NODE * select_node, XASL_NODE * xasl)
{
  if (xasl)
    {
      xasl->aptr_list =
    pt_remove_xasl (pt_append_xasl (xasl->aptr_list, pt_to_uncorr_subquery_list (parser, select_node)), xasl);
    }
}

/*
 * pt_set_connect_by_xasl() - set the CONNECT BY xasl node,
 *  and make the pseudo-columns regu vars
 *   parser(in):
 *   select_node(in):
 *   xasl(in):
 */
static XASL_NODE *
pt_set_connect_by_xasl (PARSER_CONTEXT * parser, PT_NODE * select_node, XASL_NODE * xasl)
{
  int n;
  XASL_NODE *connect_by_xasl;

  if (!xasl)
    {
      return NULL;
    }

  connect_by_xasl = pt_make_connect_by_proc (parser, select_node, xasl);
  if (!connect_by_xasl)
    {
      return xasl;
    }

  /* set the CONNECT BY pointer and flag */
  xasl->connect_by_ptr = connect_by_xasl;
  XASL_SET_FLAG (xasl, XASL_HAS_CONNECT_BY);

  /* make regu vars for use for pseudo-columns values fetching */

  n = connect_by_xasl->outptr_list->valptr_cnt;

  /* LEVEL pseudo-column */
  if (xasl->level_val)
    {
      if (!xasl->level_regu)
    {
      xasl->level_regu =
        pt_make_pos_regu_var_from_scratch (&tp_Integer_domain, xasl->level_val, n - PCOL_LEVEL_TUPLE_OFFSET);
      if (!xasl->level_regu)
        {
          return NULL;
        }
    }
    }

  /* CONNECT_BY_ISLEAF pseudo-column */
  if (xasl->isleaf_val)
    {
      if (!xasl->isleaf_regu)
    {
      xasl->isleaf_regu =
        pt_make_pos_regu_var_from_scratch (&tp_Integer_domain, xasl->isleaf_val, n - PCOL_ISLEAF_TUPLE_OFFSET);
      if (!xasl->isleaf_regu)
        {
          return NULL;
        }
    }
    }

  /* CONNECT_BY_ISCYCLE pseudo-column */
  if (xasl->iscycle_val)
    {
      if (!xasl->iscycle_regu)
    {
      xasl->iscycle_regu =
        pt_make_pos_regu_var_from_scratch (&tp_Integer_domain, xasl->iscycle_val, n - PCOL_ISCYCLE_TUPLE_OFFSET);
      if (!xasl->iscycle_regu)
        {
          return NULL;
        }
    }
    }

  /* move ORDER SIBLINGS BY column list in the CONNECT BY xasl if order_by was not cut out because of aggregates */
  if (xasl->orderby_list != NULL && select_node->info.query.flag.order_siblings == 1)
    {
      connect_by_xasl->orderby_list = pt_to_order_siblings_by (parser, xasl, connect_by_xasl);
      if (!connect_by_xasl->orderby_list)
    {
      return NULL;
    }
      xasl->orderby_list = NULL;
    }

  return xasl;
}

/*
 * pt_append_scan () - appends the from list to the end of the to list
 *   return:
 *   to(in):
 *   from(in):
 */
static XASL_NODE *
pt_append_scan (const XASL_NODE * to, const XASL_NODE * from)
{
  XASL_NODE *xasl = (XASL_NODE *) to;

  if (!xasl)
    {
      return (XASL_NODE *) from;
    }

  while (xasl->scan_ptr)
    {
      xasl = xasl->scan_ptr;
    }
  xasl->scan_ptr = (XASL_NODE *) from;

  return (XASL_NODE *) to;
}

/*
 * pt_uncorr_pre () - builds xasl list of locally correlated (level 1) queries
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in/out):
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_uncorr_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  UNCORR_INFO *info = (UNCORR_INFO *) arg;

  *continue_walk = PT_CONTINUE_WALK;

  if (!PT_IS_QUERY_NODE_TYPE (node->node_type) && node->node_type != PT_CTE)
    {
      return node;
    }

  /* Can not increment level for list portion of walk. Since those queries are not sub-queries of this query.
   * Consequently, we recurse separately for the list leading from a query.  Can't just call
   * pt_to_uncorr_subquery_list() directly since it needs to do a leaf walk and we want to do a full walk on the next
   * list. */
  if (node->next)
    {
      node->next = parser_walk_tree (parser, node->next, pt_uncorr_pre, info, pt_uncorr_post, info);
    }

  *continue_walk = PT_LEAF_WALK;

  if (node->node_type == PT_CTE)
    {
      /* don't want to include the subqueries from the PT_CTE */
      *continue_walk = PT_STOP_WALK;
    }
  /* increment level as we dive into subqueries */
  info->level++;

  return node;
}

/*
 * pt_uncorr_post () - decrement level of correlation after passing selects
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in/out):
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_uncorr_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  UNCORR_INFO *info = (UNCORR_INFO *) arg;
  XASL_NODE *xasl;

  switch (node->node_type)
    {
    case PT_SELECT:
    case PT_UNION:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
      info->level--;
      xasl = (XASL_NODE *) node->info.query.xasl;

      /* subquery or cached subquery */
      if (xasl && pt_is_subquery (node))
    {
      if (node->info.query.correlation_level == 0)
        {
          /* add to this level */
          node->info.query.correlation_level = info->level;
        }

      if (node->info.query.correlation_level == info->level)
        {
          if (node->info.query.flag.subquery_cached)
        {
          /* save the subquery cache info */
          xasl->sub_xasl_id = node->xasl_id;
          xasl->sub_host_var_count = node->sub_host_var_count;
          xasl->sub_host_var_index = node->sub_host_var_index;
        }

          /* order is important. we are on the way up, so putting things at the tail of the list will end up deeper
           * nested queries being first, which is required. */
          info->xasl = pt_append_xasl (info->xasl, xasl);
        }
    }

      break;

    case PT_CTE:
      info->level--;
      xasl = (XASL_NODE *) node->info.cte.xasl;

      if (xasl)
    {
      /* The CTE correlation level is kept in the non_recursive_part query and it is handled here since
       * the CTE subqueries are not accessed for correlation check;
       * After validation, the CTE XASL is added to the list */

      PT_NODE *non_recursive_part = node->info.cte.non_recursive_part;
      // non_recursive_part can become PT_VALUE during constant folding
      assert (PT_IS_QUERY (non_recursive_part) || PT_IS_VALUE_NODE (non_recursive_part));

      /*
         We need to check a false-query and a select-null query by constant folding.
         A false-query and a select-null query are distinguished as follows.
         false-query's node_type is PT_VALUE and type_enum is PT_TYPE_NULL.
         In select-null query, node_type is PT_SELECT and type_enum is PT_TYPE_NULL.
         false-query is not necessary to append xasl because it's not a query.
       */
      if (PT_IS_VALUE_NODE (non_recursive_part))
        {
          if (non_recursive_part->type_enum != PT_TYPE_NULL)
        {
          info->xasl = pt_append_xasl (xasl, info->xasl);
        }
          break;
        }

      if (non_recursive_part->info.query.correlation_level == 0)
        {
          /* add non_recursive_part to this level */
          non_recursive_part->info.query.correlation_level = info->level;
        }

      if (non_recursive_part->info.query.correlation_level == info->level)
        {
          /* append the CTE xasl at the beginning of the list */
          info->xasl = pt_append_xasl (xasl, info->xasl);
        }
    }

      break;

    default:
      break;
    }

  return node;
}

/*
 * pt_to_uncorr_subquery_list () - Gather the correlated level > 1 subqueries
 *  include nested queries, such that nest level + 2 = correlation level
 *  exclude the node being passed in
 *   return:
 *   parser(in):
 *   node(in):
 */
static XASL_NODE *
pt_to_uncorr_subquery_list (PARSER_CONTEXT * parser, PT_NODE * node)
{
  UNCORR_INFO info;

  info.xasl = NULL;
  info.level = 2;

  node = parser_walk_leaves (parser, node, pt_uncorr_pre, &info, pt_uncorr_post, &info);

  return info.xasl;
}

/*
 * pt_corr_pre () - builds xasl list of locally correlated (level 1) queries
 *  directly reachable. (no nested queries, which are already handled)
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
pt_corr_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  XASL_NODE *xasl;
  CORR_INFO *info = (CORR_INFO *) arg;

  *continue_walk = PT_CONTINUE_WALK;

  switch (node->node_type)
    {
    case PT_SELECT:
    case PT_UNION:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
      *continue_walk = PT_LIST_WALK;
      xasl = (XASL_NODE *) node->info.query.xasl;

      if (xasl && node->info.query.correlation_level == 1 && (info->id == MATCH_ALL || node->spec_ident == info->id))
    {
      info->xasl_head = pt_append_xasl (xasl, info->xasl_head);
    }

    default:
      break;
    }

  return node;
}

/*
 * pt_to_corr_subquery_list () - Gather the correlated level == 1 subqueries.
 *  exclude nested queries. including the node being passed in
 *   return:
 *   parser(in):
 *   node(in):
 *   id(in):
 */
static XASL_NODE *
pt_to_corr_subquery_list (PARSER_CONTEXT * parser, PT_NODE * node, UINTPTR id)
{
  CORR_INFO info;

  info.xasl_head = NULL;
  info.id = id;

  node = parser_walk_tree (parser, node, pt_corr_pre, &info, pt_continue_walk, NULL);

  return info.xasl_head;
}

/*
 * pt_link_regu_to_selupd_list () - Link update related regu list from outlist
 *                                  into selupd list of XASL tree
 *   return:
 *   parser(in):
 *   regulist(in):
 *   selupd_list(in):
 *   target_class(in):
 */
static SELUPD_LIST *
pt_link_regu_to_selupd_list (PARSER_CONTEXT * parser, REGU_VARIABLE_LIST regulist, SELUPD_LIST * selupd_list,
                 DB_OBJECT * target_class)
{
  SELUPD_LIST *node;
  REGU_VARLIST_LIST l_regulist;
  OID *oid_ptr;
  HFID *hfid_ptr;
  int is_partition = 0;

  oid_ptr = ws_identifier (target_class);
  hfid_ptr = sm_get_ch_heap (target_class);

  if (oid_ptr == NULL || hfid_ptr == NULL)
    {
      return NULL;
    }

  /* find a related info node for the target class */
  for (node = selupd_list; node != NULL; node = node->next)
    {
      if (OID_EQ (&node->class_oid, oid_ptr))
    break;
    }
  if (node == NULL)
    {
      regu_alloc (node);
      if (node == NULL)
    {
      return NULL;
    }
      if (sm_partitioned_class_type (target_class, &is_partition, NULL, NULL) != NO_ERROR)
    {
      return NULL;
    }
      if (is_partition != DB_NOT_PARTITIONED_CLASS)
    {
      /* if target class is a partitioned class, the class to access will be determimed at execution time. so do
       * not set class oid and hfid */
      OID_SET_NULL (&node->class_oid);
      HFID_SET_NULL (&node->class_hfid);
    }
      else
    {
      /* setup class info */
      COPY_OID (&node->class_oid, oid_ptr);
      HFID_COPY (&node->class_hfid, hfid_ptr);
    }

      /* insert the node into the selupd list */
      if (selupd_list == NULL)
    {
      selupd_list = node;
    }
      else
    {
      node->next = selupd_list;
      selupd_list = node;
    }
    }

  regu_alloc (l_regulist);
  if (l_regulist == NULL)
    {
      return NULL;
    }

  /* link the regulist of outlist to the node */
  l_regulist->list = regulist;

  /* add the regulist pointer to the current node */
  l_regulist->next = node->select_list;
  node->select_list = l_regulist;
  node->select_list_size++;

  return selupd_list;
}

/*
 * pt_to_outlist () - Convert a pt_node list to an outlist (of regu_variables)
 *   return:
 *   parser(in):
 *   node_list(in):
 *   selupd_list_ptr(in):
 *   unbox(in):
 */
static OUTPTR_LIST *
pt_to_outlist (PARSER_CONTEXT * parser, PT_NODE * node_list, SELUPD_LIST ** selupd_list_ptr, UNBOX unbox)
{
  OUTPTR_LIST *outlist;
  PT_NODE *node = NULL, *node_next, *col;
  int count = 0;
  REGU_VARIABLE *regu;
  REGU_VARIABLE_LIST *regulist;
  PT_NODE *save_node = NULL, *save_next = NULL;
  XASL_NODE *xasl = NULL;
  QFILE_SORTED_LIST_ID *srlist_id;
  QPROC_DB_VALUE_LIST value_list = NULL;
  int i;
  bool skip_hidden;
  PT_NODE *new_node_list = NULL;
  int list_len = 0;
  PT_NODE *cur;

  regu_alloc (outlist);
  if (outlist == NULL)
    {
      PT_ERRORm (parser, node_list, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      goto exit_on_error;
    }

  regulist = &outlist->valptrp;

  /* link next_row for PT_VALUE,PT_NAME,PT_EXPR... in PT_NODE_LIST */
  if (node_list && node_list->node_type == PT_NODE_LIST)
    {
      node = node_list->info.node_list.list;
      while (node)
    {
#if 0               /* TODO - */
      assert (node->type_enum != PT_TYPE_NULL);
#endif
      ++list_len;
      node = node->next;
    }

      /* new list head_nodes */
      new_node_list = (PT_NODE *) pt_alloc_packing_buf (list_len * sizeof (PT_NODE));
      if (new_node_list == NULL)
    {
      PT_ERRORm (parser, node_list, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      goto exit_on_error;
    }

      for (i = 0, node = node_list->info.node_list.list; i < list_len && node; ++i, node = node->next)
    {
      new_node_list[i].node_type = PT_NODE_LIST;
      parser_reinit_node (&new_node_list[i]);   /* type must be set before init */

      new_node_list[i].info.node_list.list = node;
      PT_SET_VALUE_QUERY (&new_node_list[i]);

      if (i == list_len - 1)
        {
          new_node_list[i].next = NULL;
        }
      else
        {
          new_node_list[i].next = &new_node_list[i + 1];
        }
    }

      /* link next_row pointer */
      for (node = node_list; node && node->next; node = node->next)
    {
      /* column count of rows are checked in semantic_check.c */
      for (cur = node->info.node_list.list, node_next = node->next->info.node_list.list; cur && node_next;
           cur = cur->next, node_next = node_next->next)
        {
          cur->next_row = node_next;
        }
    }

      assert (node);

      /* Now node points to the last row of node_list set the last row's next_row pointer to NULL */
      for (cur = node->info.node_list.list; cur != NULL; cur = cur->next)
    {
      cur->next_row = NULL;
    }

      node_list = new_node_list;
    }

  for (node = node_list, node_next = node ? node->next : NULL; node != NULL;
       node = node_next, node_next = node ? node->next : NULL)
    {
      save_node = node;     /* save */

      CAST_POINTER_TO_NODE (node);
      if (node)
    {
#if 0               /* TODO - */
      assert (node->type_enum != PT_TYPE_NULL);
#endif

      /* reset flag for new node */
      skip_hidden = false;

      /* save and cut-off node link */
      save_next = node->next;
      node->next = NULL;

      /* get column list */
      col = node;
      if (PT_IS_QUERY_NODE_TYPE (node->node_type))
        {
          /* hidden columns from subquery should not get referenced in select list */
          skip_hidden = true;

          xasl = (XASL_NODE *) node->info.query.xasl;
          if (xasl == NULL)
        {
          goto exit_on_error;
        }

          xasl->is_single_tuple = (unbox != UNBOX_AS_TABLE);
          if (xasl->is_single_tuple)
        {
          col = pt_get_select_list (parser, node);
          if (!xasl->single_tuple)
            {
              xasl->single_tuple = pt_make_val_list (parser, col);
              if (xasl->single_tuple == NULL)
            {
              PT_ERRORm (parser, col, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
              goto exit_on_error;
            }
            }

          value_list = xasl->single_tuple->valp;
        }
        }

      /* make outlist */
      for (i = 0; col; col = col->next, i++)
        {
#if 0               /* TODO - */
          assert (col->type_enum != PT_TYPE_NULL);
#endif

          if (skip_hidden && col->flag.is_hidden_column && i > 0)
        {
          /* we don't need this node; also, we assume the first column of the subquery is NOT hidden */
          continue;
        }

          regu_alloc (*regulist);
          if (*regulist == NULL)
        {
          goto exit_on_error;
        }

          if (PT_IS_QUERY_NODE_TYPE (node->node_type))
        {
          regu_alloc (regu);
          if (regu == NULL)
            {
              goto exit_on_error;
            }

          if (i == 0)
            {
              /* set as linked to regu var */
              XASL_SET_FLAG (xasl, XASL_LINK_TO_REGU_VARIABLE);
              regu->xasl = xasl;
            }

          if (xasl->is_single_tuple)
            {
              regu->type = TYPE_CONSTANT;
              regu->domain = pt_xasl_node_to_domain (parser, col);
              regu->value.dbvalptr = value_list->val;
              /* move to next db_value holder */
              value_list = value_list->next;
              if (pt_prepare_corr_subquery_hash_result_cache (parser, node, xasl))
            {
              XASL_SET_FLAG (xasl, XASL_USES_SQ_CACHE);
            }
            }
          else
            {
              regu_alloc (srlist_id);
              if (srlist_id == NULL)
            {
              goto exit_on_error;
            }

              regu->type = TYPE_LIST_ID;
              regu->value.srlist_id = srlist_id;
              srlist_id->list_id = xasl->list_id;
            }
        }
          else if (col->node_type == PT_EXPR && col->info.expr.op == PT_ORDERBY_NUM)
        {
          regu_alloc (regu);
          if (regu == NULL)
            {
              goto exit_on_error;
            }

          regu->type = TYPE_ORDERBY_NUM;
          regu->domain = pt_xasl_node_to_domain (parser, col);
          regu->value.dbvalptr = (DB_VALUE *) col->etc;
        }
          else
        {
          regu = pt_to_regu_variable (parser, col, unbox);
        }

          if (regu == NULL)
        {
          goto exit_on_error;
        }

#if 0               /* TODO - */
          assert (TP_DOMAIN_TYPE (regu->domain) != DB_TYPE_NULL);
#endif

          /* append to outlist */
          (*regulist)->value = *regu;

          /* in case of increment expr, find a target class to do the expr, and link the regulist to a node which
           * contains update info for the target class */
          if (selupd_list_ptr != NULL && col->node_type == PT_EXPR
          && (col->info.expr.op == PT_INCR || col->info.expr.op == PT_DECR))
        {
          PT_NODE *upd_obj;
          PT_NODE *upd_dom;
          PT_NODE *upd_dom_nm;
          DB_OBJECT *upd_dom_cls;
          OID nulloid;

          upd_obj = col->info.expr.arg2;
          if (upd_obj == NULL)
            {
              goto exit_on_error;
            }

          upd_dom = (upd_obj->node_type == PT_DOT_) ? upd_obj->info.dot.arg2->data_type : upd_obj->data_type;
          if (upd_dom == NULL)
            {
              goto exit_on_error;
            }

          if (upd_obj->type_enum != PT_TYPE_OBJECT || upd_dom->info.data_type.virt_type_enum != PT_TYPE_OBJECT)
            {
              goto exit_on_error;
            }

          upd_dom_nm = upd_dom->info.data_type.entity;
          if (upd_dom_nm == NULL)
            {
              goto exit_on_error;
            }

          upd_dom_cls = upd_dom_nm->info.name.db_object;

          /* initialize result of regu expr */
          OID_SET_NULL (&nulloid);
          db_make_oid (regu->value.arithptr->value, &nulloid);

          (*selupd_list_ptr) = pt_link_regu_to_selupd_list (parser, *regulist, (*selupd_list_ptr), upd_dom_cls);
          if ((*selupd_list_ptr) == NULL)
            {
              goto exit_on_error;
            }
        }
          regulist = &(*regulist)->next;

          count++;
        }           /* for (i = 0; ...) */

      /* restore node link */
      node->next = save_next;
    }

      node = save_node;     /* restore */
    }

  outlist->valptr_cnt = count;

  return outlist;

exit_on_error:

  /* restore node link */
  if (node)
    {
      node->next = save_next;
    }

  node = save_node;     /* restore */

  return NULL;
}


/*
 * pt_to_fetch_as_scan_proc () - Translate a PT_NODE path entity spec to an
 *      a left outer scan proc on a list file from an xasl proc
 *   return:
 *   parser(in):
 *   spec(in):
 *   pred(in):
 *   join_term(in):
 *   xasl_to_scan(in):
 */
static XASL_NODE *
pt_to_fetch_as_scan_proc (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * join_term, XASL_NODE * xasl_to_scan)
{
  XASL_NODE *xasl;
  PT_NODE *saved_current_class;
  REGU_VARIABLE *regu;
  REGU_VARIABLE_LIST regu_attributes_pred, regu_attributes_rest;
  ACCESS_SPEC_TYPE *access;
  UNBOX unbox;
  TABLE_INFO *tbl_info;
  PRED_EXPR *where = NULL;
  PT_NODE *pred_attrs = NULL, *rest_attrs = NULL;
  int *pred_offsets = NULL, *rest_offsets = NULL;

  xasl = regu_xasl_node_alloc (SCAN_PROC);
  if (!xasl)
    {
      PT_ERROR (parser, spec,
        msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      return NULL;
    }

  unbox = UNBOX_AS_VALUE;

  xasl->val_list = pt_to_val_list (parser, spec->info.spec.id);

  tbl_info = pt_find_table_info (spec->info.spec.id, parser->symbols->table_info);

  if (pt_split_attrs (parser, tbl_info, join_term, &pred_attrs, &rest_attrs, NULL, &pred_offsets, &rest_offsets, NULL)
      != NO_ERROR)
    {
      return NULL;
    }

  /* This generates a list of TYPE_POSITION regu_variables There information is stored in a QFILE_TUPLE_VALUE_POSITION,
   * which describes a type and index into a list file. */
  regu_attributes_pred = pt_to_position_regu_variable_list (parser, pred_attrs, tbl_info->value_list, pred_offsets);
  regu_attributes_rest = pt_to_position_regu_variable_list (parser, rest_attrs, tbl_info->value_list, rest_offsets);

  parser_free_tree (parser, pred_attrs);
  parser_free_tree (parser, rest_attrs);
  free_and_init (pred_offsets);
  free_and_init (rest_offsets);

  parser->symbols->listfile_unbox = unbox;
  parser->symbols->current_listfile = NULL;

  /* The where predicate is now evaluated after the val list has been fetched.  This means that we want to generate
   * "CONSTANT" regu variables instead of "POSITION" regu variables which would happen if
   * parser->symbols->current_listfile != NULL. pred should never user the current instance for fetches either, so we
   * turn off the current_class, if there is one. */
  saved_current_class = parser->symbols->current_class;
  parser->symbols->current_class = NULL;
  where = pt_to_pred_expr (parser, join_term);
  parser->symbols->current_class = saved_current_class;

  access =
    pt_make_list_access_spec (xasl_to_scan, ACCESS_METHOD_SEQUENTIAL, NULL, where, regu_attributes_pred,
                  regu_attributes_rest, NULL, NULL);

  if (access)
    {
      xasl->spec_list = access;

      access->single_fetch = QPROC_SINGLE_OUTER;

      regu = pt_join_term_to_regu_variable (parser, join_term);

      if (regu)
    {
      if (regu->type == TYPE_CONSTANT || regu->type == TYPE_DBVAL)
        access->s_dbval = pt_regu_to_dbvalue (parser, regu);
    }
    }
  parser->symbols->listfile_unbox = UNBOX_AS_VALUE;

  return xasl;
}


/*
 * pt_to_fetch_proc () - Translate a PT_NODE path entity spec to
 *                       an OBJFETCH_PROC
 *   return:
 *   parser(in):
 *   spec(in):
 *   pred(in):
 */
XASL_NODE *
pt_to_fetch_proc (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * pred)
{
  XASL_NODE *xasl = NULL;
  PT_NODE *oid_name = NULL;
  REGU_VARIABLE *regu;
  PT_NODE *flat;
  PT_NODE *conjunct;
  PT_NODE *derived;

  if (!spec)
    {
      return NULL;      /* no error */
    }

  if (spec->node_type == PT_SPEC && (conjunct = spec->info.spec.path_conjuncts) && (conjunct->node_type == PT_EXPR)
      && (oid_name = conjunct->info.expr.arg1))
    {
      flat = spec->info.spec.flat_entity_list;
      if (flat)
    {
      xasl = regu_xasl_node_alloc (OBJFETCH_PROC);

      if (xasl)
        {
          FETCH_PROC_NODE *fetch = &xasl->proc.fetch;

          xasl->next = NULL;

          xasl->outptr_list = pt_to_outlist (parser, spec->info.spec.referenced_attrs, NULL, UNBOX_AS_VALUE);

          if (xasl->outptr_list == NULL)
        {
          goto exit_on_error;
        }

          xasl->spec_list = pt_to_class_spec_list (parser, spec, NULL, pred, NULL, NULL);

          if (xasl->spec_list == NULL)
        {
          goto exit_on_error;
        }

          xasl->val_list = pt_to_val_list (parser, spec->info.spec.id);

          /* done in last if_pred, for now */
          fetch->set_pred = NULL;

          /* set flag for INNER path fetches */
          fetch->ql_flag = (QL_FLAG) (spec->info.spec.meta_class == PT_PATH_INNER);

          /* fill in xasl->proc.fetch set oid argument to DB_VALUE of left side of dot expression */
          regu = pt_attribute_to_regu (parser, oid_name);
          fetch->arg = NULL;
          if (regu)
        {
          fetch->arg = pt_regu_to_dbvalue (parser, regu);
        }
        }
      else
        {
          PT_ERROR (parser, spec,
            msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC,
                    MSGCAT_SEMANTIC_OUT_OF_MEMORY));
          return NULL;
        }
    }
      else if ((derived = spec->info.spec.derived_table))
    {
      /* this is a derived table path spec */
      xasl = pt_to_fetch_as_scan_proc (parser, spec, conjunct, (XASL_NODE *) derived->info.query.xasl);
    }
    }

  return xasl;

exit_on_error:

  return NULL;
}


/*
 * pt_to_fetch_proc_list_recurse () - Translate a PT_NODE path (dot) expression
 *  to a XASL OBJFETCH or SETFETCH proc
 *   return:
 *   parser(in):
 *   spec(in):
 *   root(in):
 */
static void
pt_to_fetch_proc_list_recurse (PARSER_CONTEXT * parser, PT_NODE * spec, XASL_NODE * root)
{
  XASL_NODE *xasl = NULL;

  xasl = pt_to_fetch_proc (parser, spec, NULL);

  if (!xasl)
    {
      return;
    }

  if (xasl->type == SCAN_PROC)
    {
      APPEND_TO_XASL (root, scan_ptr, xasl);
    }
  else
    {
      APPEND_TO_XASL (root, bptr_list, xasl);
    }

  /* get the rest of the fetch procs at this level */
  if (spec->next)
    {
      pt_to_fetch_proc_list_recurse (parser, spec->next, root);
    }

  if (xasl && spec->info.spec.path_entities)
    {
      pt_to_fetch_proc_list_recurse (parser, spec->info.spec.path_entities, root);
    }

  return;
}

/*
 * pt_to_fetch_proc_list () - Translate a PT_NODE path (dot) expression to
 *  a XASL OBJFETCH or SETFETCH proc
 *   return: none
 *   parser(in):
 *   spec(in):
 *   root(in):
 */
static void
pt_to_fetch_proc_list (PARSER_CONTEXT * parser, PT_NODE * spec, XASL_NODE * root)
{
  XASL_NODE *xasl = NULL;

  pt_to_fetch_proc_list_recurse (parser, spec, root);

  xasl = root->scan_ptr;
  if (xasl)
    {
      while (xasl->scan_ptr)
    {
      xasl = xasl->scan_ptr;
    }

      /* we must promote the if_pred to the fetch as scan proc Only do this once, not recursively */
      xasl->if_pred = root->if_pred;
      root->if_pred = NULL;
      xasl->dptr_list = root->dptr_list;
      root->dptr_list = NULL;
    }

  return;
}


/*
 * ptqo_to_scan_proc () - Convert a spec pt_node to a SCAN_PROC
 *   return:
 *   parser(in):
 *   plan(in):
 *   xasl(in):
 *   spec(in):
 *   where_key_part(in):
 *   where_part(in):
 *   info(in):
 */
XASL_NODE *
ptqo_to_scan_proc (PARSER_CONTEXT * parser, QO_PLAN * plan, XASL_NODE * xasl, PT_NODE * spec, PT_NODE * where_key_part,
           PT_NODE * where_part, QO_XASL_INDEX_INFO * info, PT_NODE * where_hash_part)
{
  if (xasl == NULL)
    {
      xasl = regu_xasl_node_alloc (SCAN_PROC);
    }

  if (!xasl)
    {
      PT_ERROR (parser, spec,
        msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      return NULL;
    }

  if (spec != NULL)
    {
      xasl->spec_list = pt_to_spec_list (parser, spec, where_key_part, where_part, plan, info, NULL, where_hash_part);
      if (xasl->spec_list == NULL)
    {
      goto exit_on_error;
    }

      xasl->val_list = pt_to_val_list (parser, spec->info.spec.id);
    }

  return xasl;

exit_on_error:

  return NULL;
}


/*
 * pt_skeleton_buildlist_proc () - Construct a partly
 *                                 initialized BUILDLIST_PROC
 *   return:
 *   parser(in):
 *   namelist(in):
 */
XASL_NODE *
pt_skeleton_buildlist_proc (PARSER_CONTEXT * parser, PT_NODE * namelist)
{
  XASL_NODE *xasl;

  assert (parser != NULL);

  xasl = regu_xasl_node_alloc (BUILDLIST_PROC);
  if (xasl == NULL)
    {
      goto exit_on_error;
    }

  xasl->outptr_list = pt_to_outlist (parser, namelist, NULL, UNBOX_AS_VALUE);
  if (xasl->outptr_list == NULL)
    {
      goto exit_on_error;
    }

  return xasl;

exit_on_error:

  return NULL;
}


/*
 * ptqo_to_list_scan_proc () - Convert an spec pt_node to a SCAN_PROC
 *   return:
 *   parser(in):
 *   xasl(in):
 *   proc_type(in):
 *   listfile(in):
 *   namelist(in):
 *   pred(in):
 *   poslist(in):
 */
XASL_NODE *
ptqo_to_list_scan_proc (PARSER_CONTEXT * parser, XASL_NODE * xasl, PROC_TYPE proc_type, XASL_NODE * listfile,
            PT_NODE * namelist, PT_NODE * pred, int *poslist)
{
  if (xasl == NULL)
    {
      xasl = regu_xasl_node_alloc (proc_type);
    }

  if (xasl && listfile)
    {
      PRED_EXPR *pred_expr = NULL;
      REGU_VARIABLE_LIST regu_attributes = NULL;
      PT_NODE *saved_current_class;
      int *attr_offsets;

      parser->symbols->listfile_unbox = UNBOX_AS_VALUE;
      parser->symbols->current_listfile = NULL;

      /* The where predicate is now evaluated after the val list has been fetched.  This means that we want to generate
       * "CONSTANT" regu variables instead of "POSITION" regu variables which would happen if
       * parser->symbols->current_listfile != NULL. pred should never user the current instance for fetches either, so
       * we turn off the current_class, if there is one. */
      saved_current_class = parser->symbols->current_class;
      parser->symbols->current_class = NULL;
      pred_expr = pt_to_pred_expr (parser, pred);
      parser->symbols->current_class = saved_current_class;

      /* Need to create a value list using the already allocated DB_VALUE data buckets on some other XASL_PROC's val
       * list. Actually, these should be simply global, but aren't. */
      xasl->val_list = pt_clone_val_list (parser, namelist);

      /* handle the buildlist case. append regu to the out_list, and create a new value to append to the value_list */
      attr_offsets = pt_make_identity_offsets (namelist);
      regu_attributes = pt_to_position_regu_variable_list (parser, namelist, xasl->val_list, attr_offsets);

      /* hack for the case of list scan in merge join */
      if (poslist)
    {
      REGU_VARIABLE_LIST p;
      int i;

      for (p = regu_attributes, i = 0; p; p = p->next, i++)
        {
          p->value.value.pos_descr.pos_no = poslist[i];
        }
    }
      free_and_init (attr_offsets);

      xasl->spec_list =
    pt_make_list_access_spec (listfile, ACCESS_METHOD_SEQUENTIAL, NULL, pred_expr, regu_attributes, NULL, NULL,
                  NULL);

      if (xasl->spec_list == NULL || xasl->val_list == NULL)
    {
      xasl = NULL;
    }
    }
  else
    {
      xasl = NULL;
    }

  return xasl;
}


/*
 * ptqo_to_merge_list_proc () - Make a MERGELIST_PROC to merge an inner
 *                              and outer list
 *   return:
 *   parser(in):
 *   left(in):
 *   right(in):
 *   join_type(in):
 */
XASL_NODE *
ptqo_to_merge_list_proc (PARSER_CONTEXT * parser, XASL_NODE * left, XASL_NODE * right, JOIN_TYPE join_type)
{
  XASL_NODE *xasl;

  assert (parser != NULL);

  if (left == NULL || right == NULL)
    {
      return NULL;
    }

  xasl = regu_xasl_node_alloc (MERGELIST_PROC);

  if (!xasl)
    {
      PT_NODE dummy;

      memset (&dummy, 0, sizeof (dummy));
      PT_ERROR (parser, &dummy,
        msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      return NULL;
    }

  xasl->proc.mergelist.outer_xasl = left;
  xasl->proc.mergelist.inner_xasl = right;

  if (join_type == JOIN_RIGHT)
    {
      right->next = left;
      xasl->aptr_list = right;
    }
  else
    {
      left->next = right;
      xasl->aptr_list = left;
    }

  return xasl;
}


/*
 * pt_to_hashjoin_proc() -
 *   return: XASL node for hash join execution; NULL on error.
 *   parser(in): Parser context.
 *   outer_xasl(in): XASL node for outer input of the hash join.
 *   inner_xasl(in): XASL node for inner input of the hash join.
 */
XASL_NODE *
pt_to_hashjoin_proc (PARSER_CONTEXT * parser, XASL_NODE * outer_xasl, XASL_NODE * inner_xasl)
{
  XASL_NODE *xasl;
  HASHJOIN_PROC_NODE *proc;

  assert (parser != NULL);
  assert (outer_xasl != NULL);
  assert (inner_xasl != NULL);

  xasl = regu_xasl_node_alloc (HASHJOIN_PROC);
  if (xasl == NULL)
    {
      assert_release_error (er_errid () != NO_ERROR);
      return NULL;
    }

  outer_xasl->next = inner_xasl;
  inner_xasl->next = NULL;

  xasl->aptr_list = outer_xasl;

  proc = &xasl->proc.hashjoin;
  proc->outer.xasl = outer_xasl;
  proc->inner.xasl = inner_xasl;

  return xasl;
}


/*
 * ptqo_single_orderby () - Make a SORT_LIST that will sort the given column
 *  according to the type of the given name
 *   return:
 *   parser(in):
 */
SORT_LIST *
ptqo_single_orderby (PARSER_CONTEXT * parser)
{
  SORT_LIST *list;

  regu_alloc (list);
  if (list)
    {
      list->next = NULL;
    }

  return list;
}


/*
 * pt_to_scan_proc_list () - Convert a SELECT pt_node to an XASL_NODE
 *                       list of SCAN_PROCs
 *   return:
 *   parser(in):
 *   node(in):
 *   root(in):
 */
static XASL_NODE *
pt_to_scan_proc_list (PARSER_CONTEXT * parser, PT_NODE * node, XASL_NODE * root)
{
  XASL_NODE *xasl = NULL;
  XASL_NODE *list = NULL;
  XASL_NODE *last = root;
  PT_NODE *from;

  from = node->info.query.q.select.from->next;

  while (from)
    {
      xasl = ptqo_to_scan_proc (parser, NULL, NULL, from, NULL, NULL, NULL, NULL);

      pt_to_pred_terms (parser, node->info.query.q.select.where, from->info.spec.id, &xasl->if_pred);

      pt_set_dptr (parser, node->info.query.q.select.where, xasl, from->info.spec.id);
      pt_set_dptr (parser, node->info.query.q.select.list, xasl, from->info.spec.id);

      if (!xasl)
    {
      return NULL;
    }

      if (from->info.spec.path_entities)
    {
      pt_to_fetch_proc_list (parser, from->info.spec.path_entities, xasl);
    }

      pt_set_dptr (parser, from->info.spec.derived_table, last, MATCH_ALL);

      last = xasl;

      from = from->next;

      /* preserve order for maintenance & sanity */
      list = pt_append_scan (list, xasl);
    }

  return list;
}

/*
 * pt_gen_optimized_plan () - Translate a PT_SELECT node to a XASL plan
 *   return:
 *   parser(in):
 *   select_node(in):
 *   plan(in):
 *   xasl(in):
 */
static XASL_NODE *
pt_gen_optimized_plan (PARSER_CONTEXT * parser, PT_NODE * select_node, QO_PLAN * plan, XASL_NODE * xasl)
{
  XASL_NODE *ret = NULL;

  assert (parser != NULL);

  if (xasl && select_node && !pt_has_error (parser))
    {
      ret = qo_to_xasl (plan, xasl);

      if (ret == NULL)
    {
      xasl->spec_list = NULL;
      xasl->scan_ptr = NULL;
    }
      else
    {
      /* if the user asked for a descending scan, force it on all iscans */
      if (select_node->info.query.q.select.hint & PT_HINT_USE_IDX_DESC)
        {
          XASL_NODE *ptr;
          for (ptr = xasl; ptr; ptr = ptr->scan_ptr)
        {
          if (ptr->spec_list && ptr->spec_list->indexptr)
            {
              ptr->spec_list->indexptr->use_desc_index = 1;
            }
        }
        }

      if (select_node->info.query.q.select.hint & PT_HINT_NO_IDX_DESC)
        {
          XASL_NODE *ptr;
          for (ptr = xasl; ptr; ptr = ptr->scan_ptr)
        {
          if (ptr->spec_list && ptr->spec_list->indexptr)
            {
              ptr->spec_list->indexptr->use_desc_index = 0;
            }
        }
        }

      /* check direction of the first order by column. see also scan_get_index_oidset() in scan_manager.c */
      if (xasl->spec_list && select_node->info.query.order_by && xasl->spec_list->indexptr)
        {
          PT_NODE *ob = select_node->info.query.order_by;

          if (ob->info.sort_spec.asc_or_desc == PT_DESC)
        {
          xasl->spec_list->indexptr->orderby_desc = 1;
        }
        }

      /* check direction of the first group by column. see also scan_get_index_oidset() in scan_manager.c */
      if (xasl->spec_list && select_node->info.query.q.select.group_by && xasl->spec_list->indexptr)
        {
          PT_NODE *gb = select_node->info.query.q.select.group_by;

          if (gb->info.sort_spec.asc_or_desc == PT_DESC)
        {
          xasl->spec_list->indexptr->groupby_desc = 1;
        }
        }

      /* if the user asked for NO_HASH_LIST_SCAN, force it on all list scan */
      if (select_node->info.query.q.select.hint & PT_HINT_NO_HASH_LIST_SCAN)
        {
          XASL_NODE *ptr;
          for (ptr = xasl; ptr; ptr = ptr->scan_ptr)
        {
          if (ptr->spec_list && ptr->spec_list->type == TARGET_LIST)
            {
              ptr->spec_list->s.list_node.hash_list_scan_yn = 0;
            }
        }
        }

      if (select_node->info.query.q.select.hint & PT_HINT_NLJ_KEEP_HEAP_PAGE_PINNED)
        {
          if (xasl->spec_list)
        {
          ACCESS_SPEC_SET_FLAG (xasl->spec_list, ACCESS_SPEC_FLAG_FORCE_FIXED_SCAN);
        }
        }
    }
    }

  return ret;
}

/*
 * pt_gen_simple_plan () - Translate a PT_SELECT node to a XASL plan
 *   return:
 *   parser(in):
 *   select_node(in):
 *   plan(in):
 *   xasl(in):
 */
static XASL_NODE *
pt_gen_simple_plan (PARSER_CONTEXT * parser, PT_NODE * select_node, QO_PLAN * plan, XASL_NODE * xasl)
{
  PT_NODE *from, *where;
  PT_NODE *access_part, *if_part, *instnum_part;
  XASL_NODE *lastxasl;
  int flag;

  assert (parser != NULL);

  if (xasl && select_node && !pt_has_error (parser))
    {
      from = select_node->info.query.q.select.from;

      /* copy so as to preserve parse tree */
      where = parser_copy_tree_list (parser, select_node->info.query.q.select.where);

      /* set 'etc' field for pseudocolumn nodes in WHERE pred */
      if (select_node->info.query.q.select.connect_by)
    {
      pt_set_level_node_etc (parser, where, &xasl->level_val);
      pt_set_isleaf_node_etc (parser, where, &xasl->isleaf_val);
      pt_set_iscycle_node_etc (parser, where, &xasl->iscycle_val);
      pt_set_connect_by_operator_node_etc (parser, where, xasl);
      pt_set_qprior_node_etc (parser, where, xasl);
    }

      pt_split_access_if_instnum (parser, from, where, &access_part, &if_part, &instnum_part);

      xasl->spec_list = pt_to_spec_list (parser, from, NULL, access_part, NULL, NULL, NULL, NULL);
      if (xasl->spec_list == NULL)
    {
      goto exit_on_error;
    }

      /* save where part to restore tree later */
      where = select_node->info.query.q.select.where;
      select_node->info.query.q.select.where = if_part;

      pt_to_pred_terms (parser, if_part, from->info.spec.id, &xasl->if_pred);

      /* and pick up any uncorrelated terms */
      pt_to_pred_terms (parser, if_part, 0, &xasl->if_pred);

      xasl = pt_to_instnum_pred (parser, xasl, instnum_part);

      if (from->info.spec.path_entities)
    {
      pt_to_fetch_proc_list (parser, from->info.spec.path_entities, xasl);
    }

      /* Find the last scan proc. Some pseudo-fetch procs may be on this list */
      lastxasl = xasl;
      while (lastxasl && lastxasl->scan_ptr)
    {
      lastxasl = lastxasl->scan_ptr;
    }

      /* if pseudo fetch procs are there, the dptr must be attached to the last xasl scan proc. */
      pt_set_dptr (parser, select_node->info.query.q.select.where, lastxasl, from->info.spec.id);

      /* this also correctly places correlated subqueries for derived tables */
      lastxasl->scan_ptr = pt_to_scan_proc_list (parser, select_node, lastxasl);

      while (lastxasl && lastxasl->scan_ptr)
    {
      lastxasl = lastxasl->scan_ptr;
    }

      /* make sure all scan_ptrs are found before putting correlated subqueries from the select list on the last
       * (inner) scan_ptr. because they may be correlated to specs later in the from list. */
      pt_set_dptr (parser, select_node->info.query.q.select.list, lastxasl, 0);

      xasl->val_list = pt_to_val_list (parser, from->info.spec.id);

      parser_free_tree (parser, access_part);
      parser_free_tree (parser, if_part);
      parser_free_tree (parser, instnum_part);
      select_node->info.query.q.select.where = where;
    }

  return xasl;

exit_on_error:

  return NULL;
}


/*
 * pt_gen_simple_merge_plan () - Translate a PT_SELECT node to a XASL plan
 *   return:
 *   parser(in):
 *   select_node(in):
 *   plan(in):
 *   xasl(in):
 */
XASL_NODE *
pt_gen_simple_merge_plan (PARSER_CONTEXT * parser, PT_NODE * select_node, QO_PLAN * plan, XASL_NODE * xasl)
{
  PT_NODE *table1, *table2;
  PT_NODE *where;
  PT_NODE *if_part, *instnum_part;
  int flag;

  assert (parser != NULL);

  if (xasl && select_node && !pt_has_error (parser) && (table1 = select_node->info.query.q.select.from)
      && (table2 = select_node->info.query.q.select.from->next) && !select_node->info.query.q.select.from->next->next)
    {
      xasl->spec_list = pt_to_spec_list (parser, table1, NULL, NULL, plan, NULL, NULL, NULL);
      if (xasl->spec_list == NULL)
    {
      goto exit_on_error;
    }

      xasl->merge_spec = pt_to_spec_list (parser, table2, NULL, NULL, plan, NULL, table1, NULL);
      if (xasl->merge_spec == NULL)
    {
      goto exit_on_error;
    }

      if (table1->info.spec.path_entities)
    {
      pt_to_fetch_proc_list (parser, table1->info.spec.path_entities, xasl);
    }

      if (table2->info.spec.path_entities)
    {
      pt_to_fetch_proc_list (parser, table2->info.spec.path_entities, xasl);
    }

      /* Correctly place correlated subqueries for derived tables. */
      if (table1->info.spec.derived_table)
    {
      pt_set_dptr (parser, table1->info.spec.derived_table, xasl, table1->info.spec.id);
    }

      /* There are two cases for table2: 1) if table1 is a derived table, then if table2 is correlated then it is
       * correlated to table1.  2) if table1 is not derived then if table2 is correlated, then it correlates to the
       * merge block. Case 2 should never happen for rewritten queries that contain method calls, but we include it
       * here for completeness. */
      if (table1->info.spec.derived_table && table1->info.spec.derived_table_type == PT_IS_SUBQUERY)
    {
      XASL_NODE *t_xasl;

      if (!(t_xasl = (XASL_NODE *) table1->info.spec.derived_table->info.query.xasl))
        {
          PT_INTERNAL_ERROR (parser, "generate plan");
          goto exit_on_error;
        }

      pt_set_dptr (parser, table2->info.spec.derived_table, t_xasl, table2->info.spec.id);
    }
      else
    {
      pt_set_dptr (parser, table2->info.spec.derived_table, xasl, table2->info.spec.id);
    }

      xasl->val_list = pt_to_val_list (parser, table1->info.spec.id);
      xasl->merge_val_list = pt_to_val_list (parser, table2->info.spec.id);

      /* copy so as to preserve parse tree */
      where = parser_copy_tree_list (parser, select_node->info.query.q.select.where);

      /* set 'etc' field for pseudocolumn nodes */
      pt_set_level_node_etc (parser, where, &xasl->level_val);
      pt_set_isleaf_node_etc (parser, where, &xasl->isleaf_val);
      pt_set_iscycle_node_etc (parser, where, &xasl->iscycle_val);
      pt_set_connect_by_operator_node_etc (parser, where, xasl);
      pt_set_qprior_node_etc (parser, where, xasl);

      pt_split_if_instnum (parser, where, &if_part, &instnum_part);

      /* This is NOT temporary till where clauses get sorted out!!! We never want predicates on the scans of the tables
       * because merge depend on both tables having the same cardinality which would get screwed up if we pushed
       * predicates down into the table scans. */
      pt_to_pred_terms (parser, if_part, table1->info.spec.id, &xasl->if_pred);
      pt_to_pred_terms (parser, if_part, table2->info.spec.id, &xasl->if_pred);

      xasl = pt_to_instnum_pred (parser, xasl, instnum_part);
      pt_set_dptr (parser, if_part, xasl, MATCH_ALL);

      pt_set_dptr (parser, select_node->info.query.q.select.list, xasl, MATCH_ALL);

      parser_free_tree (parser, if_part);
      parser_free_tree (parser, instnum_part);
    }

  return xasl;

exit_on_error:

  return NULL;
}

/*
 * pt_to_buildschema_proc () - Translate a schema PT_SELECT node to
 *                           a XASL buildschema proc
 *   return:
 *   parser(in):
 *   select_node(in): the query node
 */
static XASL_NODE *
pt_to_buildschema_proc (PARSER_CONTEXT * parser, PT_NODE * select_node)
{
  XASL_NODE *xasl = NULL;
  SYMBOL_INFO *symbols = NULL;
  UNBOX unbox;
  PT_NODE *flat = NULL, *from = NULL;
  ACCESS_SCHEMA_TYPE acces_schema_type;

  symbols = parser->symbols;
  if (symbols == NULL)
    {
      return NULL;
    }

  if (select_node == NULL || select_node->node_type != PT_SELECT)
    {
      return NULL;
    }

  from = select_node->info.query.q.select.from;
  if (from == NULL)
    {
      return NULL;
    }

  flat = from->info.spec.flat_entity_list;
  if (flat == NULL)
    {
      return NULL;
    }

  xasl = regu_xasl_node_alloc (BUILD_SCHEMA_PROC);
  if (xasl == NULL)
    {
      PT_ERRORm (parser, select_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      return NULL;
    }

  xasl->next = NULL;
  xasl->option = Q_ALL;
  unbox = UNBOX_AS_VALUE;

  xasl->flag = 0;
  xasl->after_iscan_list = NULL;

  if (PT_SELECT_INFO_IS_FLAGED (select_node, PT_SELECT_INFO_IDX_SCHEMA))
    {
      xasl->orderby_list = pt_to_orderby (parser, select_node->info.query.order_by, select_node);
      if (xasl->orderby_list == NULL)
    {
      goto error_exit;
    }
      xasl->ordbynum_flag = XASL_ORDBYNUM_FLAG_SCAN_CONTINUE;
    }

  xasl->ordbynum_pred = NULL;
  xasl->ordbynum_val = NULL;
  xasl->orderby_limit = NULL;
  xasl->orderby_limit = NULL;

  xasl->single_tuple = NULL;
  xasl->is_single_tuple = 0;
  xasl->outptr_list = pt_to_outlist (parser, select_node->info.query.q.select.list, &xasl->selected_upd_list, unbox);

  if (PT_SELECT_INFO_IS_FLAGED (select_node, PT_SELECT_INFO_COLS_SCHEMA))
    {
      acces_schema_type = COLUMNS_SCHEMA;
    }
  else if (PT_SELECT_INFO_IS_FLAGED (select_node, PT_SELECT_FULL_INFO_COLS_SCHEMA))
    {
      acces_schema_type = FULL_COLUMNS_SCHEMA;
    }
  else
    {
      acces_schema_type = INDEX_SCHEMA;
    }

  xasl->spec_list =
    pt_make_class_access_spec (parser, flat, flat->info.name.db_object, TARGET_CLASS, ACCESS_METHOD_SCHEMA, NULL, NULL,
                   NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
                   acces_schema_type, NULL, NULL);

  if (xasl->spec_list == NULL)
    {
      goto error_exit;
    }

  xasl->merge_spec = NULL;
  xasl->val_list = NULL;
  xasl->merge_val_list = NULL;
  xasl->aptr_list = NULL;
  xasl->bptr_list = NULL;
  xasl->dptr_list = NULL;
  xasl->during_join_pred = NULL;
  xasl->after_join_pred = NULL;
  xasl->if_pred = NULL;
  xasl->instnum_pred = NULL;
  xasl->instnum_val = NULL;
  xasl->save_instnum_val = NULL;
  xasl->fptr_list = NULL;
  xasl->scan_ptr = NULL;
  xasl->connect_by_ptr = NULL;
  xasl->level_val = NULL;
  xasl->level_regu = NULL;
  xasl->isleaf_val = NULL;
  xasl->isleaf_regu = NULL;
  xasl->iscycle_val = NULL;
  xasl->iscycle_regu = NULL;
  xasl->curr_spec = NULL;
  xasl->instnum_flag = 0;

  return xasl;

error_exit:

  return NULL;
}

/*
 * pt_analytic_to_metadomain () - initialize metadomain for analytic function
 *                  and index the sort list elements
 *   returns: true if successful, false if sort spec index overflows
 *   func_p(in): analytic function
 *   sort_list(in): sort list of analytic function
 *   func_meta(in): metadomain to be initialized
 *   index(in/out): sort spec index
 *   index_size(in/out): sort spec index size
 */
static bool
pt_analytic_to_metadomain (ANALYTIC_TYPE * func_p, PT_NODE * sort_list, ANALYTIC_KEY_METADOMAIN * func_meta,
               PT_NODE ** index, int *index_size)
{
  PT_NODE *list;
  int i, idx;

  assert (func_p != NULL && func_meta != NULL && index != NULL);

  /* structure initialization */
  func_meta->level = 0;
  func_meta->key_size = 0;
  func_meta->links_count = 0;
  func_meta->demoted = false;
  func_meta->children[0] = NULL;
  func_meta->children[1] = NULL;
  func_meta->source = func_p;
  func_meta->part_size = func_p->sort_prefix_size;

  /* build index and structure */
  for (list = sort_list; list != NULL; list = list->next)
    {
      /* search for sort list in index */
      idx = -1;
      for (i = 0; i < (*index_size); i++)
    {
      if (SORT_SPEC_EQ (index[i], list))
        {
          /* found */
          idx = i;
          break;
        }
    }

      /* add to index if not present */
      if (idx == -1)
    {
      if ((*index_size) >= ANALYTIC_OPT_MAX_SORT_LIST_COLUMNS)
        {
          /* no more space in index */
          return false;
        }

      idx = (*index_size);
      index[idx] = list;
      (*index_size)++;
    }

      /* register */
      func_meta->key[func_meta->key_size] = idx;
      func_meta->key_size++;
      func_meta->level++;

      if (func_meta->key_size >= ANALYTIC_OPT_MAX_FUNCTIONS)
    {
      /* no more space in  index */
      return false;
    }
    }

  /* all ok */
  return true;
}

/*
 * pt_metadomains_compatible () - determine if two metadomains are compatible
 *                (i.e. can be evaluated together)
 *   returns: compatibility as boolean
 *   f1/f2(in): the two metadomains
 *   out(in): output (common) metadomain to be populated
 *   lost_link_count(out): the number of compatibility links that are lost by
 *             combining the two metadomains
 *   level(in): maximum sort list size to consider in compatibility checking
 *
 * NOTE: Given two window definitions like the following (* denotes a partition
 *       by column, # denotes an order by column):
 *
 *   f1: * * * * # # # # #
 *   f2: * * # # # # #
 *  out: * * # # # # # # #
 *       ^-^--------------------------- common partition prefix (CPP)
 *           ^-^----------------------- forced partition suffix (FPS)
 *               ^-^-^----------------- common sort list (CSL)
 *                     ^-^------------- sort list suffix (SLS)
 *
 * The two windows can share a sort list iff:
 *   a. CSL(f1) == CSL(f2)
 *   b. {CPP(f2)} + {FPS(f2)} is a subset of {FPS(f1)} + {CPP(f1)}
 *
 * The resulting window (out) will have the structure:
 *  out = CPP(f2) + FPS(f2) + CSL(f1) + SLS(f1) i.e.
 *  out = f2 + SLS(f1)
 */
static bool
pt_metadomains_compatible (ANALYTIC_KEY_METADOMAIN * f1, ANALYTIC_KEY_METADOMAIN * f2, ANALYTIC_KEY_METADOMAIN * out,
               int *lost_link_count, int level)
{
  unsigned int f1_fps_cpp = 0, f2_fps_cpp = 0;
  int i, j;
  bool found;
  ANALYTIC_TYPE *analytic1 = NULL, *analytic2 = NULL;

  assert (f1 != NULL && f2 != NULL);

  if (lost_link_count != NULL)
    {
      /* initialize to default value in case of failure */
      (*lost_link_count) = -1;
    }

  /* determine larger key */
  if (f1->part_size < f2->part_size)
    {
      ANALYTIC_KEY_METADOMAIN *aux = f1;
      f1 = f2;
      f2 = aux;
    }

  analytic1 = f1->source;
  analytic2 = f2->source;

  /* step (a): compare common sort lists */
  for (i = f1->part_size; i < MIN (level, MIN (f1->key_size, f2->key_size)); i++)
    {
      if (f1->key[i] != f2->key[i])
    {
      return false;
    }
    }

  /* step (b) */
  for (i = 0; i < MIN (level, f1->part_size); i++)
    {
      f1_fps_cpp |= 1 << f1->key[i];
    }
  for (i = 0; i < MIN (level, MIN (f1->part_size, f2->key_size)); i++)
    {
      f2_fps_cpp |= 1 << f2->key[i];
    }
  if ((f2_fps_cpp & f1_fps_cpp) != f2_fps_cpp)
    {
      /* f2_fps_cpp is not a subset of f1_fps_cpp */
      return false;
    }

  /* interpolation function with string arg type is not compatible with other functions */
  if (analytic1 != NULL && analytic2 != NULL)
    {
      if (QPROC_IS_INTERPOLATION_FUNC (analytic1) && QPROC_IS_INTERPOLATION_FUNC (analytic2)
      && (f1->part_size != f2->part_size
          || (TP_IS_STRING_TYPE (analytic1->opr_dbtype) ^ TP_IS_STRING_TYPE (analytic2->opr_dbtype))))
    {
      return false;
    }
      else if (QPROC_IS_INTERPOLATION_FUNC (analytic1) && !QPROC_IS_INTERPOLATION_FUNC (analytic2)
           && TP_IS_STRING_TYPE (analytic1->opr_dbtype))
    {
      return false;
    }
      else if (QPROC_IS_INTERPOLATION_FUNC (analytic2) && !QPROC_IS_INTERPOLATION_FUNC (analytic1)
           && TP_IS_STRING_TYPE (analytic2->opr_dbtype))
    {
      return false;
    }
    }

  if (out == NULL || lost_link_count == NULL)
    {
      /* no need to compute common metadomain */
      return true;
    }

  /* build common metadomain */
  out->source = NULL;
  out->links_count = 0;
  out->level = level;
  out->demoted = false;
  out->children[0] = f1;
  out->children[1] = f2;
  out->part_size = MIN (f2->part_size, level);
  out->key_size = MIN (MAX (f1->key_size, f2->key_size), level);

  for (i = 0; i < out->key_size; i++)
    {
      if (i < f2->key_size)
    {
      /* get from f2 */
      out->key[i] = f2->key[i];
      if (i < f1->part_size)
        {
          /* current key element cannot be used further */
          f1_fps_cpp &= ~(1 << f2->key[i]);
        }
    }
      else
    {
      if (i >= f1->part_size)
        {
          /* original order (SLS) */
          out->key[i] = f1->key[i];
        }
      else
        {
          bool found = false;

          /* whatever order from what's left in {CPP(f1)} + {FPS(f1)} */
          for (j = 0; j < ANALYTIC_OPT_MAX_SORT_LIST_COLUMNS; j++)
        {
          if (f1_fps_cpp & (1 << j))
            {
              out->key[i] = j;
              f1_fps_cpp &= ~(1 << j);
              found = true;
              break;
            }
        }
          if (!found)
        {
          assert (false);
          /* make sure corrupted struct is not used */
          return false;
        }
        }
    }
    }

  /* build links */
  (*lost_link_count) = 0;

  for (i = 0; i < f1->links_count; i++)
    {
      if (f1->links[i] == f2)
    {
      continue;
    }
      else if (pt_metadomains_compatible (out, f1->links[i], NULL, NULL, level))
    {
      out->links[out->links_count++] = f1->links[i];
    }
      else
    {
      (*lost_link_count)++;
    }
    }
  for (i = 0; i < f2->links_count; i++)
    {
      found = false;
      for (j = 0; j < f1->links_count; j++)
    {
      if (f1->links[j] == f2->links[i])
        {
          found = true;
          break;
        }
    }

      if ((f2->links[i] == f1) || found)
    {
      continue;
    }
      else
    {
      if (pt_metadomains_compatible (out, f2->links[i], NULL, NULL, level))
        {
          out->links[out->links_count++] = f2->links[i];
        }
      else
        {
          (*lost_link_count)++;
        }
    }
    }

  /* all ok */
  return true;
}

/*
 * pt_metadomain_build_comp_graph () - build metadomain compatibility graph of
 *                     all analytic functions
 *   af_meta(in): analytic function meta domain list
 *   af_count(in): analytic function count
 *   level(in): maximum size of considered sort list
 */
static void
pt_metadomain_build_comp_graph (ANALYTIC_KEY_METADOMAIN * af_meta, int af_count, int level)
{
  int i, j;

  assert (af_meta != NULL);

  /* reset link count */
  for (i = 0; i < af_count; i++)
    {
      af_meta[i].links_count = 0;
    }

  /* check compatibility */
  for (i = 0; i < af_count; i++)
    {
      if (af_meta[i].demoted)
    {
      continue;
    }

      for (j = i + 1; j < af_count; j++)
    {
      if (af_meta[j].demoted)
        {
          continue;
        }

      if (pt_metadomains_compatible (&af_meta[i], &af_meta[j], NULL, NULL, level))
        {
          /* now kiss */
          af_meta[i].links[af_meta[i].links_count] = &af_meta[j];
          af_meta[j].links[af_meta[j].links_count] = &af_meta[i];
          af_meta[i].links_count++;
          af_meta[j].links_count++;
        }
    }
    }
}

/*
 * pt_sort_list_from_metadomain () - build sort list for metadomain
 *   returns: sort list or NULL on error
 *   parser(in): parser context
 *   sort_list_index(in): index of sort specs
 *   select_list(in): select list of query
 */
static SORT_LIST *
pt_sort_list_from_metadomain (PARSER_CONTEXT * parser, ANALYTIC_KEY_METADOMAIN * meta, PT_NODE ** sort_list_index,
                  PT_NODE * select_list)
{
  PT_NODE *sort_list_pt = NULL;
  SORT_LIST *sort_list;
  int i;

  assert (meta != NULL && sort_list_index != NULL);

  /* build PT_NODE list */
  for (i = 0; i < meta->key_size; i++)
    {
      PT_NODE *copy = parser_copy_tree (parser, sort_list_index[meta->key[i]]);
      if (copy == NULL)
    {
      PT_INTERNAL_ERROR (parser, "copy tree");
      (void) parser_free_tree (parser, sort_list_pt);
      return NULL;
    }
      else
    {
      copy->next = NULL;
      sort_list_pt = parser_append_node (copy, sort_list_pt);
    }
    }

  if (sort_list_pt != NULL)
    {
      sort_list = pt_to_sort_list (parser, sort_list_pt, select_list, SORT_LIST_ANALYTIC_WINDOW);
      parser_free_tree (parser, sort_list_pt);

      if (sort_list == NULL)
    {
      PT_INTERNAL_ERROR (parser, "sort list generation");
    }

      return sort_list;
    }
  else
    {
      return NULL;
    }
}

/*
 * pt_metadomain_adjust_key_prefix () - adjust children's sort key using parent's sort key
 *   meta(in): metadomain
 */
static void
pt_metadomain_adjust_key_prefix (ANALYTIC_KEY_METADOMAIN * meta)
{
  int i, child;

  assert (meta != NULL);

  for (child = 0; child < 2; child++)
    {
      if (meta->children[child])
    {
      for (i = 0; i < meta->level; i++)
        {
          if (i < meta->children[child]->key_size)
        {
          meta->children[child]->key[i] = meta->key[i];
        }
        }

      pt_metadomain_adjust_key_prefix (meta->children[child]);
    }
    }
}

/*
 * pt_build_analytic_eval_list () - build evaluation sequence based on computed
 *                  metadomains
 *   returns: (partial) evaluation sequence
 *   parser(in): parser context
 *   meta(in): metadomain
 *   eval(in): eval structure (i.e. sequence component)
 *   sort_list_index(in): index of sort specs
 *   info(in): analytic info structure
 */
static ANALYTIC_EVAL_TYPE *
pt_build_analytic_eval_list (PARSER_CONTEXT * parser, QO_PLAN * qo_plan, ANALYTIC_KEY_METADOMAIN * meta,
                 ANALYTIC_EVAL_TYPE * eval, PT_NODE ** sort_list_index, ANALYTIC_INFO * info)
{
  ANALYTIC_EVAL_TYPE *newa = NULL, *new2 = NULL, *tail;
  ANALYTIC_TYPE *func_p;

  assert (meta != NULL && info != NULL);

  if (meta->children[0] && meta->children[1])
    {
      if (meta->level >= meta->children[0]->level && meta->level >= meta->children[1]->level)
    {
      if (eval == NULL)
        {
          regu_alloc (eval);
          if (eval == NULL)
        {
          PT_INTERNAL_ERROR (parser, "regu alloc");
          return NULL;
        }

          eval->sort_list = pt_sort_list_from_metadomain (parser, meta, sort_list_index, info->select_list);
          if (meta->key_size > 0 && eval->sort_list == NULL)
        {
          /* error was already set */
          return NULL;
        }
          eval->sort_list_size = meta->key_size;
          eval->covered_size = pt_count_analytic_covered_sort_list (parser, qo_plan, eval, info);
        }

      /* this is the case of a perfect match where both children can be evaluated together */
      eval = pt_build_analytic_eval_list (parser, qo_plan, meta->children[0], eval, sort_list_index, info);
      if (eval == NULL)
        {
          /* error was already set */
          return NULL;
        }

      eval = pt_build_analytic_eval_list (parser, qo_plan, meta->children[1], eval, sort_list_index, info);
      if (eval == NULL)
        {
          /* error was already set */
          return NULL;
        }
    }
      else
    {
      if (meta->level >= meta->children[0]->level)
        {
          eval = pt_build_analytic_eval_list (parser, qo_plan, meta->children[0], eval, sort_list_index, info);
          if (eval == NULL)
        {
          /* error was already set */
          return NULL;
        }

          newa = pt_build_analytic_eval_list (parser, qo_plan, meta->children[1], NULL, sort_list_index, info);
          if (newa == NULL)
        {
          /* error was already set */
          return NULL;
        }
        }
      else if (meta->level >= meta->children[1]->level)
        {
          eval = pt_build_analytic_eval_list (parser, qo_plan, meta->children[1], eval, sort_list_index, info);
          if (eval == NULL)
        {
          /* error was already set */
          return NULL;
        }

          newa = pt_build_analytic_eval_list (parser, qo_plan, meta->children[0], NULL, sort_list_index, info);
          if (newa == NULL)
        {
          /* error was already set */
          return NULL;
        }
        }
      else
        {
          newa = pt_build_analytic_eval_list (parser, qo_plan, meta->children[0], NULL, sort_list_index, info);
          if (newa == NULL)
        {
          /* error was already set */
          return NULL;
        }

          new2 = pt_build_analytic_eval_list (parser, qo_plan, meta->children[1], NULL, sort_list_index, info);
          if (new2 == NULL)
        {
          /* error was already set */
          return NULL;
        }
        }

      if (newa != NULL && new2 != NULL)
        {
          if (newa->covered_size < new2->covered_size)
        {
          ANALYTIC_EVAL_TYPE *tmp = newa;
          newa = new2;
          new2 = tmp;
        }
          /* link new to new2 */
          tail = newa;
          while (tail->next != NULL)
        {
          tail = tail->next;
        }
          tail->next = new2;
        }

      if (eval == NULL)
        {
          eval = newa;
        }
      else
        {
          if (eval->covered_size < newa->covered_size)
        {
          ANALYTIC_EVAL_TYPE *tmp = eval;
          eval = newa;
          newa = tmp;
        }
          /* link eval to new */
          tail = eval;
          while (tail->next != NULL)
        {
          tail = tail->next;
        }
          tail->next = newa;
        }
    }
    }
  else
    {
      /* this is a leaf node; create eval structure if necessary */
      if (eval == NULL)
    {
      regu_alloc (eval);
      if (eval == NULL)
        {
          PT_INTERNAL_ERROR (parser, "regu alloc");
          return NULL;
        }

      /* check for top level here, write sort list */
      eval->sort_list = pt_sort_list_from_metadomain (parser, meta, sort_list_index, info->select_list);
      if (meta->key_size > 0 && eval->sort_list == NULL)
        {
          /* error was already set */
          return NULL;
        }
      eval->sort_list_size = meta->key_size;
      eval->covered_size = pt_count_analytic_covered_sort_list (parser, qo_plan, eval, info);
    }

      meta->source->next = NULL;
      if (eval->head != NULL)
    {
      for (func_p = eval->head; func_p->next != NULL; func_p = func_p->next);
      func_p->next = meta->source;
    }
      else
    {
      eval->head = meta->source;
    }
    }

  return eval;
}

/*
 * pt_initialize_analytic_info () - initialize analytic_info
 *   parser(in):
 *   analytic_info(out):
 *   select_node(in):
 *   select_node_ex(in):
 *   buidlist(in):
 */
static int
pt_initialize_analytic_info (PARSER_CONTEXT * parser, ANALYTIC_INFO * analytic_info, PT_NODE * select_node,
                 PT_NODE * select_list_ex, BUILDLIST_PROC_NODE * buildlist)
{
  PT_NODE *node;
  int idx;
  QPROC_DB_VALUE_LIST vallist_p;

  assert (analytic_info != NULL);

  analytic_info->head_list = NULL;
  analytic_info->sort_lists = NULL;
  analytic_info->select_node = select_node;
  analytic_info->select_list = select_list_ex;
  analytic_info->val_list = buildlist->a_val_list;

  for (node = select_list_ex, vallist_p = buildlist->a_val_list->valp, idx = 0; node;
       node = node->next, vallist_p = vallist_p->next, idx++)
    {
      assert (vallist_p != NULL);

      if (PT_IS_ANALYTIC_NODE (node))
    {
      /* process analytic node */
      if (pt_to_analytic_node (parser, node, analytic_info) == NULL)
        {
          return ER_FAILED;
        }

      /* register vallist dbval for further use */
      analytic_info->head_list->out_value = vallist_p->val;
    }
    }

  return NO_ERROR;
}

/*
 * pt_is_analytic_eval_list_valid () - check the generated eval list
 *   eval_list(in):
 *
 * NOTE: This function checks the generated list whether it includes an invalid node.
 * This is just a quick fix and should be removed when we fix pt_optimize_analytic_list.
 */
static bool
pt_is_analytic_eval_list_valid (ANALYTIC_EVAL_TYPE * eval_list)
{
  ANALYTIC_EVAL_TYPE *p;

  assert (eval_list != NULL);

  for (p = eval_list; p != NULL; p = p->next)
    {
      if (p->head == NULL)
    {
      /* This is badly generated. We give up optimization for this invalid case. */
      return false;
    }
    }

  return true;
}

/*
 * pt_generate_simple_analytic_eval_type () - generate simple when optimization fails
 *   info(in/out): analytic info
 *
 * NOTE: This function generates one evaluation structure for an analytic function.
 */
static ANALYTIC_EVAL_TYPE *
pt_generate_simple_analytic_eval_type (PARSER_CONTEXT * parser, ANALYTIC_INFO * info)
{
  ANALYTIC_EVAL_TYPE *ret = NULL;
  ANALYTIC_TYPE *func_p, *save_next;
  PT_NODE *sort_list;

  /* build one eval group for each analytic function */
  func_p = info->head_list;
  sort_list = info->sort_lists;
  while (func_p)
    {
      ANALYTIC_EVAL_TYPE *newa = NULL;

      /* new eval structure */
      regu_alloc (newa);
      if (newa == NULL)
    {
      PT_INTERNAL_ERROR (parser, "regu alloc");
      return NULL;
    }
      else if (ret == NULL)
    {
      ret = newa;
    }
      else
    {
      newa->next = ret;
      ret = newa;
    }

      /* set up sort list */
      if (sort_list->info.pointer.node != NULL)
    {
      ret->sort_list =
        pt_to_sort_list (parser, sort_list->info.pointer.node, info->select_list, SORT_LIST_ANALYTIC_WINDOW);
      if (ret->sort_list == NULL)
        {
          /* error has already been set */
          return NULL;
        }
    }
      else
    {
      ret->sort_list = NULL;
    }

      /* one function */
      ret->head = func_p;

      /* unlink and advance */
      save_next = func_p->next;
      func_p->next = NULL;
      func_p = save_next;
      sort_list = sort_list->next;
    }

  return ret;
}

/*
 * pt_optimize_analytic_list () - optimize analytic exectution
 *   info(in/out): analytic info
 *   no_optimization(out):
 *
 * NOTE: This function groups together the evaluation of analytic functions
 * that share the same window.
 */
static ANALYTIC_EVAL_TYPE *
pt_optimize_analytic_list (PARSER_CONTEXT * parser, QO_PLAN * qo_plan, ANALYTIC_INFO * info, bool * no_optimization)
{
  ANALYTIC_EVAL_TYPE *ret = NULL;
  ANALYTIC_TYPE *func_p;
  PT_NODE *sort_list;
  bool found;
  int i, j, level = 0;

  /* sort list index */
  PT_NODE *sc_index[ANALYTIC_OPT_MAX_SORT_LIST_COLUMNS];
  int sc_count = 0;

  /* meta domains */
  ANALYTIC_KEY_METADOMAIN af_meta[ANALYTIC_OPT_MAX_FUNCTIONS * 2];
  int af_count = 0;

  assert (info != NULL);

  *no_optimization = false;

  /* find unique sort columns and index them; build analytic meta structures */
  for (func_p = info->head_list, sort_list = info->sort_lists; func_p != NULL && sort_list != NULL;
       func_p = func_p->next, sort_list = sort_list->next, af_count++)
    {
      if (af_count >= ANALYTIC_OPT_MAX_FUNCTIONS)
    {
      /* analytic function index overflow, we'll do it the old fashioned way */
      *no_optimization = true;
      return NULL;
    }

      if (!pt_analytic_to_metadomain (func_p, sort_list->info.pointer.node, &af_meta[af_count], sc_index, &sc_count))
    {
      /* sort spec index overflow, we'll do it the old fashioned way */
      *no_optimization = true;
      return NULL;
    }

      /* first level is maximum key size */
      if (level < af_meta[af_count].key_size)
    {
      level = af_meta[af_count].key_size;
    }
    }

  /* group metadomains with zero-length sort keys */
  do
    {
      found = false;
      for (i = 0; i < af_count - 1; i++)
    {
      if (!af_meta[i].demoted && af_meta[i].key_size == 0)
        {
          for (j = i + 1; j < af_count; j++)
        {
          if (!af_meta[j].demoted && af_meta[j].key_size == 0)
            {
              found = true;

              if (af_count >= ANALYTIC_OPT_MAX_FUNCTIONS)
            {
              *no_optimization = true;
              return NULL;
            }

              /* demote and register children */
              af_meta[i].demoted = true;
              af_meta[j].demoted = true;
              af_meta[af_count].children[0] = &af_meta[i];
              af_meta[af_count].children[1] = &af_meta[j];

              /* populate new metadomain */
              af_meta[af_count].demoted = false;
              af_meta[af_count].part_size = 0;
              af_meta[af_count].key_size = 0;
              af_meta[af_count].level = level;  /* maximum level */
              af_meta[af_count].links_count = 0;
              af_meta[af_count].source = NULL;

              /* repeat */
              af_count++;
              break;
            }
        }

          if (found)
        {
          break;
        }
        }
    }
    }
  while (found);

  /* build initial compatibility graph */
  pt_metadomain_build_comp_graph (af_meta, af_count, level);

  /* compose every compatible metadomains from each possible prefix length */
  while (level > 0)
    {
      ANALYTIC_KEY_METADOMAIN newa = analitic_key_metadomain_Initializer;
      ANALYTIC_KEY_METADOMAIN best = analitic_key_metadomain_Initializer;
      int new_destroyed = -1, best_destroyed = -1;

      /* compose best two compatible metadomains */
      for (i = 0; i < af_count; i++)
    {
      if (af_meta[i].links_count <= 0 || af_meta[i].demoted)
        {
          /* nothing to do for unlinked or demoted metadomain */
          continue;
        }

      for (j = 0; j < af_meta[i].links_count; j++)
        {
          /* build composite metadomain */
          pt_metadomains_compatible (&af_meta[i], af_meta[i].links[j], &newa, &new_destroyed, level);

          /* see if it's better than current best */
          if (new_destroyed < best_destroyed || best_destroyed == -1)
        {
          best_destroyed = new_destroyed;
          best = newa;
        }

          if (best_destroyed == 0)
        {
          /* early exit, perfect match */
          break;
        }
        }

      if (best_destroyed == 0)
        {
          /* early exit, perfect match */
          break;
        }
    }

      if (best_destroyed == -1)
    {
      /* no more optimizations on this level */
      level--;

      /* rebuild compatibility graph */
      pt_metadomain_build_comp_graph (af_meta, af_count, level);
    }
      else
    {
      ANALYTIC_KEY_METADOMAIN *link;

      /* add new composed metadomain */
      af_meta[af_count++] = best;

      /* unlink child metadomains */
      for (i = 0; i < best.children[0]->links_count; i++)
        {
          link = best.children[0]->links[i];
          for (j = 0; j < link->links_count; j++)
        {
          if (link->links[j] == best.children[0])
            {
              link->links[j] = link->links[link->links_count - 1];
              link->links_count--;
              break;
            }
        }
        }
      for (i = 0; i < best.children[1]->links_count; i++)
        {
          link = best.children[1]->links[i];
          for (j = 0; j < link->links_count; j++)
        {
          if (link->links[j] == best.children[1])
            {
              link->links[j] = link->links[link->links_count - 1];
              link->links_count--;
              break;
            }
        }
        }

      /* demote and unlink child metadomains */
      best.children[0]->demoted = true;
      best.children[1]->demoted = true;
      best.children[0]->links_count = 0;
      best.children[1]->links_count = 0;

      /* relink new composite metadomain */
      for (i = 0; i < best.links_count; i++)
        {
          link = best.links[i];
          link->links[link->links_count++] = &af_meta[af_count - 1];
        }

      /* adjust key prefix on tree */
      pt_metadomain_adjust_key_prefix (&best);
    }
    }

  /* rebuild analytic type list */
  ret = NULL;
  for (i = 0; i < af_count; i++)
    {
      ANALYTIC_EVAL_TYPE *newa, *tail;

      if (af_meta[i].demoted)
    {
      /* demoted metadomains have already been composed; we're interested only in top level metadomains */
      continue;
    }

      /* build new list */
      newa = pt_build_analytic_eval_list (parser, qo_plan, &af_meta[i], NULL, sc_index, info);
      if (newa == NULL)
    {
      /* error has already been set */
      return NULL;
    }

      /* attach to current list */
      if (ret == NULL)
    {
      /* first top level metadomain */
      ret = newa;
    }
      else
    {
      if (ret->covered_size < newa->covered_size)
        {
          ANALYTIC_EVAL_TYPE *tmp = ret;
          ret = newa;
          newa = tmp;
        }
      /* locate list tail */
      tail = ret;
      while (tail->next != NULL)
        {
          tail = tail->next;
        }

      /* link */
      tail->next = newa;
    }
    }

  /*
   * FIXME: This is a quick fix. Remove this when we fix pt_build_analytic_eval_list ().
   */
  if (!pt_is_analytic_eval_list_valid (ret))
    {
      /* give up optimization for the case */
      *no_optimization = true;
      return NULL;
    }

  return ret;
}


/*
 * pt_to_buildlist_proc () - Translate a PT_SELECT node to
 *                           a XASL buildlist proc
 *   return:
 *   parser(in):
 *   select_node(in):
 *   qo_plan(in):
 */
static XASL_NODE *
pt_to_buildlist_proc (PARSER_CONTEXT * parser, PT_NODE * select_node, QO_PLAN * qo_plan)
{
  XASL_NODE *xasl, *save_parent_proc_xasl;
  PT_NODE *saved_current_class;
  int groupby_ok = 1;
  AGGREGATE_TYPE *aggregate = NULL, *agg_list = NULL;
  SYMBOL_INFO *symbols;
  PT_NODE *from, *limit;
  UNBOX unbox;
  PT_NODE *having_part, *grbynum_part;
  int grbynum_flag, ordbynum_flag;
  bool orderby_skip = false, orderby_ok = true;
  bool groupby_skip = false;
  BUILDLIST_PROC_NODE *buildlist;
  int i;
  REGU_VARIABLE_LIST regu_var_p;

  assert (parser != NULL);

  symbols = parser->symbols;
  if (symbols == NULL)
    {
      return NULL;
    }

  if (select_node == NULL || select_node->node_type != PT_SELECT)
    {
      assert (false);
      return NULL;
    }

  from = select_node->info.query.q.select.from;
  if (from == NULL)
    {
      return NULL;
    }

  xasl = regu_xasl_node_alloc (BUILDLIST_PROC);
  if (xasl == NULL)
    {
      return NULL;
    }

  /* save this XASL node for children to access */
  save_parent_proc_xasl = parser->parent_proc_xasl;
  parser->parent_proc_xasl = xasl;

  buildlist = &xasl->proc.buildlist;
  xasl->next = NULL;

  xasl->limit_row_count = NULL;
  xasl->limit_offset = NULL;

  limit = select_node->info.query.limit;
  if (limit)
    {
      if (limit->next)
    {
      xasl->limit_offset = pt_to_regu_variable (parser, limit, UNBOX_AS_VALUE);
      limit = limit->next;
    }
      xasl->limit_row_count = pt_to_regu_variable (parser, limit, UNBOX_AS_VALUE);
    }

  /* set references of INST_NUM and ORDERBY_NUM values in parse tree */
  pt_set_numbering_node_etc (parser, select_node->info.query.q.select.list, &xasl->instnum_val, &xasl->ordbynum_val);
  pt_set_numbering_node_etc (parser, select_node->info.query.q.select.where, &xasl->instnum_val, &xasl->ordbynum_val);
  pt_set_numbering_node_etc (parser, select_node->info.query.orderby_for, &xasl->instnum_val, &xasl->ordbynum_val);

  /* assume parse tree correct, and PT_DISTINCT only other possibility */
  if (select_node->info.query.all_distinct == PT_ALL)
    {
      xasl->option = Q_ALL;
    }
  else
    {
      xasl->option = Q_DISTINCT;
    }

  unbox = UNBOX_AS_VALUE;

  if (pt_has_aggregate (parser, select_node))
    {
      int *attr_offsets;
      PT_NODE *group_out_list, *group, *select_out_list, *node, *new_node;

      /* set 'etc' field for pseudocolumns nodes */
      pt_set_level_node_etc (parser, select_node->info.query.q.select.group_by, &xasl->level_val);
      pt_set_isleaf_node_etc (parser, select_node->info.query.q.select.group_by, &xasl->isleaf_val);
      pt_set_iscycle_node_etc (parser, select_node->info.query.q.select.group_by, &xasl->iscycle_val);
      pt_set_connect_by_operator_node_etc (parser, select_node->info.query.q.select.group_by, xasl);
      pt_set_qprior_node_etc (parser, select_node->info.query.q.select.group_by, xasl);
      pt_set_level_node_etc (parser, select_node->info.query.q.select.having, &xasl->level_val);
      pt_set_isleaf_node_etc (parser, select_node->info.query.q.select.having, &xasl->isleaf_val);
      pt_set_iscycle_node_etc (parser, select_node->info.query.q.select.having, &xasl->iscycle_val);
      pt_set_connect_by_operator_node_etc (parser, select_node->info.query.q.select.having, xasl);
      pt_set_qprior_node_etc (parser, select_node->info.query.q.select.having, xasl);

      group_out_list = NULL;
      for (group = select_node->info.query.q.select.group_by; group; group = group->next)
    {
      /* safe guard: invalid parse tree */
      if (group->node_type != PT_SORT_SPEC)
        {
          if (group_out_list)
        {
          parser_free_tree (parser, group_out_list);
        }
          goto exit_on_error;
        }

      group_out_list = parser_append_node (pt_point (parser, group->info.sort_spec.expr), group_out_list);
    }

      /* determine if query is eligible for hash aggregate evaluation */
      if (select_node->info.query.q.select.hint & PT_HINT_NO_HASH_AGGREGATE)
    {
      /* forced not applicable */
      buildlist->g_hash_eligible = false;
    }
      else
    {
      buildlist->g_hash_eligible = true;

      (void) parser_walk_tree (parser, select_node->info.query.q.select.list, pt_is_hash_agg_eligible,
                   (void *) &buildlist->g_hash_eligible, NULL, NULL);
      (void) parser_walk_tree (parser, select_node->info.query.q.select.having, pt_is_hash_agg_eligible,
                   (void *) &buildlist->g_hash_eligible, NULL, NULL);

      /* determine where we're storing the first tuple of each group */
      if (buildlist->g_hash_eligible)
        {
          if (select_node->info.query.q.select.group_by->flag.with_rollup)
        {
          /* if using rollup groups, we must output the first tuple of each group so rollup will be correctly
           * handled during sort */
          buildlist->g_output_first_tuple = true;
        }
          else
        {
          /* in other cases just store everyting in hash table */
          buildlist->g_output_first_tuple = false;
        }
        }
    }

      /* this one will be altered further on and it's the actual output of the initial scan; will contain group key and
       * aggregate expressions */
      xasl->outptr_list = pt_to_outlist (parser, group_out_list, NULL, UNBOX_AS_VALUE);

      if (xasl->outptr_list == NULL)
    {
      if (group_out_list)
        {
          parser_free_tree (parser, group_out_list);
        }
      goto exit_on_error;
    }

      buildlist->g_val_list = pt_make_val_list (parser, group_out_list);

      if (buildlist->g_val_list == NULL)
    {
      PT_ERRORm (parser, group_out_list, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      if (group_out_list)
        {
          parser_free_tree (parser, group_out_list);
        }
      goto exit_on_error;
    }

      attr_offsets = pt_make_identity_offsets (group_out_list);
      if (buildlist->g_hash_eligible && attr_offsets)
    {
      REGU_VARIABLE_LIST reg_var_p_out;
      int i = 0;
      int group_out_list_len = pt_length_of_list (group_out_list);
      for (reg_var_p_out = xasl->outptr_list->valptrp; reg_var_p_out && i < group_out_list_len;
           reg_var_p_out = reg_var_p_out->next)
        {
          reg_var_p_out->value.vfetch_to = pt_index_value (buildlist->g_val_list, attr_offsets[i]);
          i++;
        }
    }

      /* set up hash aggregate lists */
      if (buildlist->g_hash_eligible)
    {
      /* regulist for hash key during initial scan */
      buildlist->g_hk_scan_regu_list =
        pt_to_regu_variable_list (parser, group_out_list, UNBOX_AS_VALUE, buildlist->g_val_list, attr_offsets);

      /* regulist for hash key during sort operation */
      buildlist->g_hk_sort_regu_list =
        pt_to_position_regu_variable_list (parser, group_out_list, buildlist->g_val_list, attr_offsets);
    }
      else
    {
      buildlist->g_hk_sort_regu_list = NULL;
      buildlist->g_hk_scan_regu_list = NULL;
    }

      /* this will load values from initial scan into g_val_list, a bypass of outptr_list => (listfile) => g_regu_list
       * => g_vallist; this will be modified when building aggregate nodes */
      buildlist->g_scan_regu_list =
    pt_to_regu_variable_list (parser, group_out_list, UNBOX_AS_VALUE, buildlist->g_val_list, attr_offsets);

      /* regulist for loading from listfile */
      buildlist->g_regu_list =
    pt_to_position_regu_variable_list (parser, group_out_list, buildlist->g_val_list, attr_offsets);

      pt_fix_pseudocolumns_pos_regu_list (parser, group_out_list, buildlist->g_regu_list);

      free_and_init (attr_offsets);

      /* set 'etc' field for pseudocolumns nodes */
      pt_set_level_node_etc (parser, select_node->info.query.q.select.list, &xasl->level_val);
      pt_set_isleaf_node_etc (parser, select_node->info.query.q.select.list, &xasl->isleaf_val);
      pt_set_iscycle_node_etc (parser, select_node->info.query.q.select.list, &xasl->iscycle_val);
      pt_set_connect_by_operator_node_etc (parser, select_node->info.query.q.select.list, xasl);
      pt_set_qprior_node_etc (parser, select_node->info.query.q.select.list, xasl);

      aggregate =
    pt_to_aggregate (parser, select_node, xasl->outptr_list, buildlist->g_val_list, buildlist->g_regu_list,
             buildlist->g_scan_regu_list, group_out_list, &buildlist->g_grbynum_val, qo_plan);

      /* compute function count */
      buildlist->g_func_count = 0;
      agg_list = aggregate;
      while (agg_list != NULL)
    {
      buildlist->g_func_count++;
      agg_list = agg_list->next;
    }

      /* compute hash key size */
      buildlist->g_hkey_size = 0;
      if (buildlist->g_hash_eligible)
    {
      REGU_VARIABLE_LIST regu_list = buildlist->g_hk_scan_regu_list;
      while (regu_list != NULL)
        {
          buildlist->g_hkey_size++;
          regu_list = regu_list->next;
        }
    }

      /* set current_listfile only around call to make g_outptr_list and havein_pred */
      symbols->current_listfile = group_out_list;
      symbols->listfile_value_list = buildlist->g_val_list;

      select_out_list = NULL;
      for (node = select_node->info.query.q.select.list; node; node = node->next)
    {
      new_node =
        pt_make_result_ref (parser, node, select_node->info.query.q.select.group_by, buildlist->g_val_list);

      if (pt_has_error (parser))
        {
          if (group_out_list)
        {
          parser_free_tree (parser, group_out_list);
        }
          if (select_out_list)
        {
          parser_free_tree (parser, select_out_list);
        }
          goto exit_on_error;
        }

      if (new_node == NULL)
        {
          new_node = pt_point (parser, node);
          if (new_node == NULL)
        {
          if (group_out_list)
            {
              parser_free_tree (parser, group_out_list);
            }
          if (select_out_list)
            {
              parser_free_tree (parser, select_out_list);
            }
          goto exit_on_error;
        }
        }

      select_out_list = parser_append_node (new_node, select_out_list);
    }

      buildlist->g_outptr_list = pt_to_outlist (parser, select_out_list, NULL, unbox);

      if (buildlist->g_outptr_list == NULL)
    {
      if (group_out_list)
        {
          parser_free_tree (parser, group_out_list);
        }
      goto exit_on_error;
    }

      if (select_out_list)
    {
      parser_free_tree (parser, select_out_list);
    }

      /* pred should never user the current instance for fetches either, so we turn off the current_class, if there is
       * one. */
      saved_current_class = parser->symbols->current_class;
      parser->symbols->current_class = NULL;
      pt_split_having_grbynum (parser, select_node->info.query.q.select.having, &having_part, &grbynum_part);
      buildlist->g_having_pred = pt_to_pred_expr (parser, having_part);
      grbynum_flag = 0;
      buildlist->g_grbynum_pred = pt_to_pred_expr_with_arg (parser, grbynum_part, &grbynum_flag);
      if (grbynum_flag & PT_PRED_ARG_GRBYNUM_CONTINUE)
    {
      buildlist->g_grbynum_flag = XASL_G_GRBYNUM_FLAG_SCAN_CONTINUE;
    }
      if (grbynum_part != NULL && PT_EXPR_INFO_IS_FLAGED (grbynum_part, PT_EXPR_INFO_GROUPBYNUM_LIMIT))
    {
      if (grbynum_part->next != NULL)
        {
          buildlist->g_grbynum_flag |= XASL_G_GRBYNUM_FLAG_LIMIT_GT_LT;
        }
      else
        {
          buildlist->g_grbynum_flag |= XASL_G_GRBYNUM_FLAG_LIMIT_LT;
        }
    }

      select_node->info.query.q.select.having = parser_append_node (having_part, grbynum_part);

      parser->symbols->current_class = saved_current_class;
      symbols->current_listfile = NULL;
      symbols->listfile_value_list = NULL;
      if (group_out_list)
    {
      parser_free_tree (parser, group_out_list);
    }

      buildlist->g_agg_list = aggregate;

      buildlist->g_with_rollup = select_node->info.query.q.select.group_by->flag.with_rollup;
    }
  else
    {
      /* set 'etc' field for pseudocolumns nodes */
      pt_set_level_node_etc (parser, select_node->info.query.q.select.list, &xasl->level_val);
      pt_set_isleaf_node_etc (parser, select_node->info.query.q.select.list, &xasl->isleaf_val);
      pt_set_iscycle_node_etc (parser, select_node->info.query.q.select.list, &xasl->iscycle_val);
      pt_set_connect_by_operator_node_etc (parser, select_node->info.query.q.select.list, xasl);
      pt_set_qprior_node_etc (parser, select_node->info.query.q.select.list, xasl);

      if (!pt_has_analytic (parser, select_node))
    {
      xasl->outptr_list =
        pt_to_outlist (parser, select_node->info.query.q.select.list, &xasl->selected_upd_list, unbox);
    }
      else
    {
      /* the select list will be altered a lot in the following code block, make sure you understand what's
       * happening before making adjustments */

      ANALYTIC_INFO analytic_info, analytic_info_clone;
      PT_NODE *select_list_ex = NULL, *select_list_final = NULL, *node;
      int idx, final_idx, final_count, *sort_adjust = NULL;
      bool no_optimization_done = false;

      /* prepare sort adjustment array */
      final_idx = 0;
      final_count = pt_length_of_list (select_node->info.query.q.select.list);
      sort_adjust = (int *) db_private_alloc (NULL, final_count * sizeof (int));
      if (sort_adjust == NULL)
        {
          PT_ERRORm (parser, select_list_ex, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          goto analytic_exit_on_error;
        }

      /* break up expressions with analytic functions */
      select_list_ex = NULL;
      select_list_final = NULL;

      node = select_node->info.query.q.select.list;
      select_node->info.query.q.select.list = NULL;

      while (node != NULL)
        {
          PT_NODE *final_node, *to_ex_list = NULL, *save_next;

          /* save next and unlink node */
          save_next = node->next;
          node->next = NULL;

          /* get final select list node */
          final_node = pt_to_analytic_final_node (parser, node, &to_ex_list, &xasl->instnum_flag);
          if (final_node == NULL)
        {
          /* error was set somewhere - clean up */
          parser_free_tree (parser, node);
          parser_free_tree (parser, save_next);
          parser_free_tree (parser, to_ex_list);

          goto analytic_exit_on_error;
        }

          /* append nodes to list */
          select_list_ex = parser_append_node (to_ex_list, select_list_ex);
          select_list_final = parser_append_node (final_node, select_list_final);

          /* modify sort spec adjustment counter to account for new nodes */
          assert (final_idx < final_count);
          sort_adjust[final_idx] = -1;  /* subtracted 1 for original node */
          for (; to_ex_list != NULL; to_ex_list = to_ex_list->next)
        {
          /* add one for each node that goes in extended list */
          sort_adjust[final_idx] += 1;
        }

          /* advance */
          node = save_next;
          final_idx++;
        }

      /* adjust sort specs of analytics in select_list_ex */
      for (node = select_list_final, idx = 0, final_idx = 0; node != NULL && final_idx < final_count;
           node = node->next, final_idx++)
        {
          PT_NODE *list;

          /* walk list and adjust */
          for (list = select_list_ex; list; list = list->next)
        {
          pt_adjust_analytic_sort_specs (parser, list, idx, sort_adjust[final_idx]);
        }

          /* increment and adjust index too */
          idx += sort_adjust[final_idx] + 1;
        }

      /* we now have all analytics as top-level nodes in select_list_ex; allocate DB_VALUEs for them and push sort
       * cols and parameters in select_list_ex */
      for (node = select_list_ex; node; node = node->next)
        {
          if (PT_IS_ANALYTIC_NODE (node))
        {
          /* allocate a DB_VALUE in node's etc */
          if (pt_set_analytic_node_etc (parser, node) == NULL)
            {
              goto analytic_exit_on_error;
            }

          /* expand node; the select list will be modified, but that is acceptable; query sort specs must not
           * be modified as they reference positions in the final outptr list */
          if (pt_expand_analytic_node (parser, node, select_list_ex) == NULL)
            {
              goto analytic_exit_on_error;
            }
        }
        }

      /* generate buffer value list */
      buildlist->a_val_list = pt_make_val_list (parser, select_list_ex);
      if (buildlist->a_val_list == NULL)
        {
          PT_ERRORm (parser, select_list_ex, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          goto analytic_exit_on_error;
        }

      /* resolve regu pointers in final outlist to vallist */
      for (node = select_list_final; node; node = node->next)
        {
          if (pt_resolve_analytic_references (parser, node, select_list_ex, buildlist->a_val_list) == NULL)
        {
          goto analytic_exit_on_error;
        }
        }

      /* generate analytic nodes */
      if (pt_initialize_analytic_info (parser, &analytic_info, select_node, select_list_ex, buildlist) != NO_ERROR)
        {
          goto analytic_exit_on_error;
        }

      /* FIXME
       *
       * The cloned list will be used when optimization of analytic functions fails.
       * Cloning is not necessary for oridinary cases, however I just want to make the lists are same.
       * It will be removed when we fix pt_build_analytic_eval_list ().
       */
      if (pt_initialize_analytic_info (parser, &analytic_info_clone, select_node, select_list_ex, buildlist) !=
          NO_ERROR)
        {
          goto analytic_exit_on_error;
        }

      int *attr_offsets;
      attr_offsets = pt_make_identity_offsets (select_list_ex);

      buildlist->a_scan_regu_list =
        pt_to_regu_variable_list (parser, select_list_ex, UNBOX_AS_VALUE, buildlist->a_val_list, attr_offsets);

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

      /* generate regu list (identity fetching from temp tuple) */
      buildlist->a_regu_list =
        pt_to_position_regu_variable_list (parser, select_list_ex, buildlist->a_val_list, NULL);

      if (buildlist->a_regu_list == NULL)
        {
          goto analytic_exit_on_error;
        }

      /* generate intermediate output list (identity writing) */
      buildlist->a_outptr_list_interm = pt_make_outlist_from_vallist (parser, buildlist->a_val_list);

      if (buildlist->a_outptr_list_interm == NULL)
        {
          goto analytic_exit_on_error;
        }

      /* set to intermediate analytic window regu_vars */
      for (i = 0, regu_var_p = buildlist->a_outptr_list_interm->valptrp;
           i < buildlist->a_outptr_list_interm->valptr_cnt && regu_var_p; i++, regu_var_p = regu_var_p->next)
        {
          REGU_VARIABLE_SET_FLAG (&regu_var_p->value, REGU_VARIABLE_ANALYTIC_WINDOW);
        }
      assert (i == buildlist->a_outptr_list_interm->valptr_cnt);
      assert (regu_var_p == NULL);

      /* generate initial outlist (for data fetching) */
      buildlist->a_outptr_list_ex = pt_to_outlist (parser, select_list_ex, &xasl->selected_upd_list, unbox);

      if (buildlist->a_regu_list == NULL)
        {
          goto analytic_exit_on_error;
        }

      /* generate final outlist */
      buildlist->a_outptr_list = pt_to_outlist (parser, select_list_final, NULL, unbox);

      if (buildlist->a_outptr_list == NULL)
        {
          goto analytic_exit_on_error;
        }

      /* optimize analytic function list */
      xasl->proc.buildlist.a_eval_list =
        pt_optimize_analytic_list (parser, qo_plan, &analytic_info, &no_optimization_done);

      if (pt_check_analytic_limit_optimization (xasl, xasl->proc.buildlist.a_eval_list) != NO_ERROR)
        {
          PT_ERRORm (parser, select_list_ex, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          goto analytic_exit_on_error;
        }


      if (XASL_IS_FLAGED (xasl, XASL_ANALYTIC_USES_LIMIT_OPT))
        {
          PT_NODE *final_node = NULL;
          REGU_VARIABLE_LIST regu_list_new, prev = NULL;
          REGU_VARIABLE *regu_var_temp;

          for (final_node = select_list_final; final_node != NULL; final_node = final_node->next)
        {
          if (pt_is_instnum (final_node))
            {
              buildlist->a_outptr_list_ex->valptr_cnt++;

              regu_alloc (regu_list_new);
              if (regu_list_new == NULL)
            {
              goto analytic_exit_on_error;
            }

              regu_var_temp = pt_make_regu_numbering (parser, final_node);
              regu_list_new->value = *regu_var_temp;

              if (prev == NULL)
            {
              regu_list_new->next = buildlist->a_outptr_list_ex->valptrp;
              buildlist->a_outptr_list_ex->valptrp = regu_list_new;
            }
              else
            {
              regu_list_new->next = prev->next;
              prev->next = regu_list_new;
            }
              prev = regu_list_new;
            }
          else
            {
              prev = (prev != NULL) ? prev->next : buildlist->a_outptr_list_ex->valptrp;
            }
        }
        }

      /* FIXME - Fix it with pt_build_analytic_eval_list (). */
      if (no_optimization_done == true)
        {
          /* generate one per analytic function */
          xasl->proc.buildlist.a_eval_list = pt_generate_simple_analytic_eval_type (parser, &analytic_info_clone);
        }

      if (xasl->proc.buildlist.a_eval_list == NULL && analytic_info.head_list != NULL)
        {
          /* input functions were provided but optimizer messed up */
          goto analytic_exit_on_error;
        }
      else
        {
          /* register the eval list in the plan for printing purposes */
          qo_plan->analytic_eval_list = xasl->proc.buildlist.a_eval_list;
        }

      if (xasl->proc.buildlist.a_eval_list->covered_size == xasl->proc.buildlist.a_eval_list->sort_list_size
          && xasl->proc.buildlist.a_eval_list->covered_size != 0)
        {
          /* Sorting can be skipped only if a_eval_list->sort_list matches a leading prefix
           * of the index sort order (including the full index key).
           *
           * For example, index (c1, c2, c3) can satisfy sort_list (c1), (c1, c2), (c1, c2, c3), but not (c1, c3). */
          XASL_SET_FLAG (xasl, XASL_ANALYTIC_SKIP_SORT);
        }

      /* substitute references of analytic arguments */
      for (node = select_list_ex; node; node = node->next)
        {
          if (PT_IS_ANALYTIC_NODE (node))
        {
          if (node->info.function.arg_list != NULL)
            {
              node->info.function.arg_list =
            pt_substitute_analytic_references (parser, node->info.function.arg_list, &select_list_ex);
              if (node->info.function.arg_list == NULL)
            {
              goto analytic_exit_on_error;
            }
            }

          if (node->info.function.analytic.offset != NULL)
            {
              node->info.function.analytic.offset =
            pt_substitute_analytic_references (parser, node->info.function.analytic.offset,
                               &select_list_ex);
              if (node->info.function.analytic.offset == NULL)
            {
              goto analytic_exit_on_error;
            }
            }

          if (node->info.function.analytic.default_value != NULL)
            {
              node->info.function.analytic.default_value =
            pt_substitute_analytic_references (parser, node->info.function.analytic.default_value,
                               &select_list_ex);
              if (node->info.function.analytic.default_value == NULL)
            {
              goto analytic_exit_on_error;
            }
            }
        }
        }

      /* substitute references in final select list and register it as query's select list; this is done mostly for
       * printing purposes */
      node = select_list_final;
      select_list_final = NULL;
      while (node != NULL)
        {
          PT_NODE *save_next = node->next, *resolved;
          node->next = NULL;

          resolved = pt_substitute_analytic_references (parser, node, &select_list_ex);
          if (resolved == NULL)
        {
          /* error has been set */
          parser_free_tree (parser, save_next);
          parser_free_tree (parser, node);

          goto analytic_exit_on_error;
        }

          /* append to select list */
          select_node->info.query.q.select.list =
        parser_append_node (resolved, select_node->info.query.q.select.list);

          /* advance */
          node = save_next;
        }

      /* whatever we're left with in select_list_ex are sort columns of analytic functions; there might be
       * subqueries, generate aptr and dptr lists for them */
      node = select_node->info.query.q.select.list;
      select_node->info.query.q.select.list = select_list_ex;

      pt_set_aptr (parser, select_node, xasl);
      pt_set_dptr (parser, select_list_ex, xasl, MATCH_ALL);

      select_node->info.query.q.select.list = node;

      /* we can dispose of the sort columns now as they no longer serve a purpose */
      parser_free_tree (parser, select_list_ex);
      select_list_ex = NULL;

      /* register initial outlist */
      xasl->outptr_list = buildlist->a_outptr_list_ex;

      /* all done */
      goto analytic_exit;

    analytic_exit_on_error:
      /* cleanup and goto error */
      if (select_list_ex != NULL)
        {
          parser_free_tree (parser, select_list_ex);
        }
      if (select_list_final != NULL)
        {
          parser_free_tree (parser, select_list_final);
        }
      if (sort_adjust != NULL)
        {
          db_private_free (NULL, sort_adjust);
        }
      goto exit_on_error;

    analytic_exit:
      if (sort_adjust != NULL)
        {
          db_private_free (NULL, sort_adjust);
        }
      /* finalized correctly */
    }

      /* check if this select statement has click counter */
      if (xasl->selected_upd_list != NULL)
    {
      /* set lock timeout hint if specified */
      PT_NODE *hint_arg;
      float hint_wait_secs;

      xasl->selected_upd_list->wait_msecs = XASL_WAIT_MSECS_NOCHANGE;
      hint_arg = select_node->info.query.q.select.waitsecs_hint;
      if (select_node->info.query.q.select.hint & PT_HINT_LK_TIMEOUT && PT_IS_HINT_NODE (hint_arg))
        {
          hint_wait_secs = (float) atof (hint_arg->info.name.original);
          if (hint_wait_secs > 0)
        {
          xasl->selected_upd_list->wait_msecs = (int) (hint_wait_secs * 1000);
        }
          else
        {
          xasl->selected_upd_list->wait_msecs = (int) hint_wait_secs;
        }
        }
    }

      if (xasl->outptr_list == NULL)
    {
      goto exit_on_error;
    }
    }

  /* the calls pt_to_out_list and pt_to_spec_list record information in the "symbol_info" structure used by subsequent
   * calls, and must be done first, before calculating subquery lists, etc. */

  pt_set_aptr (parser, select_node, xasl);

  if (select_node->info.query.q.select.hint & PT_HINT_PARALLEL)
    {
      xasl->parallelism = select_node->info.query.q.select.num_parallel_threads;
    }
  else
    {
      xasl->parallelism = -1;   /* auto-compute */
    }

  if (select_node->info.query.q.select.hint & PT_HINT_NO_PARALLEL_SUBQUERY)
    {
      XASL_SET_FLAG (xasl, XASL_NO_PARALLEL_SUBQUERY);
    }

  if (qo_plan == NULL || !pt_gen_optimized_plan (parser, select_node, qo_plan, xasl))
    {
      while (from)
    {
      if (from->info.spec.join_type != PT_JOIN_NONE)
        {
          PT_ERRORm (parser, from, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_OUTER_JOIN_OPT_FAILED);
          goto exit_on_error;
        }
      from = from->next;
    }

      if (select_node->info.query.q.select.flavor == PT_MERGE_SELECT)
    {
      xasl = pt_gen_simple_merge_plan (parser, select_node, qo_plan, xasl);
    }
      else
    {
      xasl = pt_gen_simple_plan (parser, select_node, qo_plan, xasl);
    }

      if (xasl == NULL)
    {
      goto exit_on_error;
    }

      buildlist = &xasl->proc.buildlist;

      /* mark as simple plan generation */
      qo_plan = NULL;
    }

  if (xasl->outptr_list)
    {
      if (qo_plan)
    {           /* is optimized plan */
      xasl->after_iscan_list = pt_to_after_iscan (parser, qo_plan_iscan_sort_list (qo_plan), select_node);
    }
      else
    {
      xasl->after_iscan_list = NULL;
    }

      if (select_node->info.query.order_by)
    {
      /* set 'etc' field for pseudocolumns nodes */
      pt_set_level_node_etc (parser, select_node->info.query.orderby_for, &xasl->level_val);
      pt_set_isleaf_node_etc (parser, select_node->info.query.orderby_for, &xasl->isleaf_val);
      pt_set_iscycle_node_etc (parser, select_node->info.query.orderby_for, &xasl->iscycle_val);
      pt_set_connect_by_operator_node_etc (parser, select_node->info.query.orderby_for, xasl);
      pt_set_qprior_node_etc (parser, select_node->info.query.orderby_for, xasl);

      ordbynum_flag = 0;
      xasl->ordbynum_pred = pt_to_pred_expr_with_arg (parser, select_node->info.query.orderby_for, &ordbynum_flag);
      if (ordbynum_flag & PT_PRED_ARG_ORDBYNUM_CONTINUE)
        {
          xasl->ordbynum_flag = XASL_ORDBYNUM_FLAG_SCAN_CONTINUE;
        }

      /* check order by opt */
      if (qo_plan && !qo_plan->need_final_sort && qo_plan_skip_orderby (qo_plan)
          && !qo_plan_multi_range_opt (qo_plan))
        {
          orderby_skip = true;

          /* move orderby_num() to inst_num() */
          if (xasl->ordbynum_val)
        {
          if (xasl->instnum_pred)
            {
              PRED_EXPR *pred = pt_make_pred_expr_pred (xasl->instnum_pred,
                                xasl->ordbynum_pred, B_AND);
              if (!pred)
            {
              goto exit_on_error;
            }
              xasl->instnum_pred = pred;
            }
          else
            {
              xasl->instnum_pred = xasl->ordbynum_pred;
            }

          /* When we set instnum_val to point to the DBVALUE referenced by ordbynum_val, we lose track the
           * DBVALUE originally stored in instnum_val. This is an important value because it is referenced by
           * any regu var that was converted from ROWNUM in the select list (before we knew we were going to
           * optimize away the ORDER BY clause). We will save the dbval in save_instnum_val and update it
           * whenever we update the new instnum_val. */
          xasl->save_instnum_val = xasl->instnum_val;
          xasl->instnum_val = xasl->ordbynum_val;
          xasl->instnum_flag = xasl->ordbynum_flag;

          xasl->ordbynum_pred = NULL;
          xasl->ordbynum_val = NULL;
          xasl->ordbynum_flag = 0;
        }
        }
      else
        {
          xasl->orderby_list = pt_to_orderby (parser, select_node->info.query.order_by, select_node);
          /* clear flag */
          XASL_CLEAR_FLAG (xasl, XASL_SKIP_ORDERBY_LIST);
        }

      /* sanity check */
      orderby_ok = ((xasl->orderby_list != NULL) || orderby_skip);
    }
      else if (select_node->info.query.order_by == NULL && select_node->info.query.orderby_for != NULL
           && xasl->option == Q_DISTINCT)
    {
      ordbynum_flag = 0;
      xasl->ordbynum_pred = pt_to_pred_expr_with_arg (parser, select_node->info.query.orderby_for, &ordbynum_flag);
      if (ordbynum_flag & PT_PRED_ARG_ORDBYNUM_CONTINUE)
        {
          xasl->ordbynum_flag = XASL_ORDBYNUM_FLAG_SCAN_CONTINUE;
        }
    }

      if ((xasl->instnum_pred != NULL || xasl->instnum_flag & XASL_INSTNUM_FLAG_EVAL_DEFER)
      && pt_has_analytic (parser, select_node) && !XASL_IS_FLAGED (xasl, XASL_ANALYTIC_USES_LIMIT_OPT))
    {
      /* we have an inst_num() which should not get evaluated in the initial fetch(processing stage)
       * qexec_execute_analytic(post-processing stage) will use it in the final sort */
      xasl->instnum_flag |= XASL_INSTNUM_FLAG_SCAN_STOP_AT_ANALYTIC;
    }

      /* union fields for BUILDLIST_PROC_NODE - BUILDLIST_PROC */
      if (select_node->info.query.q.select.group_by)
    {
      if (qo_plan && qo_plan_skip_groupby (qo_plan))
        {
          groupby_skip = true;
        }

      /* finish group by processing */
      buildlist->groupby_list = pt_to_groupby (parser, select_node->info.query.q.select.group_by, select_node);

      /* Build SORT_LIST of the list file created by GROUP BY */
      buildlist->after_groupby_list =
        pt_to_after_groupby (parser, select_node->info.query.q.select.group_by, select_node);

      /* This is a having subquery list. If it has correlated subqueries, they must be run each group */
      buildlist->eptr_list = pt_to_corr_subquery_list (parser, select_node->info.query.q.select.having, 0);

      /* otherwise should be run once, at beginning. these have already been put on the aptr list above */
      groupby_ok = (buildlist->groupby_list && buildlist->g_outptr_list
            && (buildlist->g_having_pred || buildlist->g_grbynum_pred
                || !select_node->info.query.q.select.having));

      if (groupby_skip)
        {
          groupby_ok = 1;
        }

      buildlist->a_eval_list = NULL;
      buildlist->a_outptr_list = NULL;
      buildlist->a_outptr_list_ex = NULL;
      buildlist->a_regu_list = NULL;
      buildlist->a_val_list = NULL;
    }
      else
    {
      /* with no group by, a build-list proc should not be built a build-value proc should be built instead */
      buildlist->groupby_list = NULL;
      buildlist->g_regu_list = NULL;
      buildlist->g_val_list = NULL;
      buildlist->g_having_pred = NULL;
      buildlist->g_grbynum_pred = NULL;
      buildlist->g_grbynum_val = NULL;
      buildlist->g_grbynum_flag = 0;
      buildlist->g_agg_list = NULL;
      buildlist->eptr_list = NULL;
      buildlist->g_with_rollup = 0;
    }

      /* set index scan order */
      xasl->iscan_oid_order = ((orderby_skip) ? false : prm_get_bool_value (PRM_ID_BT_INDEX_SCAN_OID_ORDER));

      /* save single tuple info */
      if (select_node->info.query.flag.single_tuple == 1)
    {
      xasl->is_single_tuple = true;
    }
    }               /* end xasl->outptr_list */

  /* verify everything worked */
  if (!xasl->outptr_list || !xasl->spec_list || !xasl->val_list || !groupby_ok || !orderby_ok || pt_has_error (parser))
    {
      goto exit_on_error;
    }

  /* set CONNECT BY xasl */
  xasl = pt_set_connect_by_xasl (parser, select_node, xasl);
  if (!xasl)
    {
      goto exit_on_error;
    }

  /* convert instnum to key limit (optimization) */
  if (pt_instnum_to_key_limit (parser, qo_plan, xasl) != NO_ERROR)
    {
      goto exit_on_error;
    }

  xasl->orderby_limit = NULL;
  if (xasl->ordbynum_pred)
    {
      QO_LIMIT_INFO *limit_infop = qo_get_key_limit_from_ordbynum (parser, qo_plan, xasl, false);
      if (limit_infop)
    {
      xasl->orderby_limit = limit_infop->upper;
      db_private_free (NULL, limit_infop);
    }
    }

  if (pt_set_like_recompile_candidate (parser, qo_plan, xasl) != NO_ERROR)
    {
      goto exit_on_error;
    }

  if (pt_set_limit_optimization_flags (parser, qo_plan, xasl) != NO_ERROR)
    {
      goto exit_on_error;
    }

  /* set list file descriptor for dummy pusher */
  if (PT_SELECT_INFO_IS_FLAGED (select_node, PT_SELECT_INFO_LIST_PUSHER))
    {
      buildlist->push_list_id = select_node->info.query.q.select.push_list;
    }

  /* set flag for multi-update subquery */
  if (PT_SELECT_INFO_IS_FLAGED (select_node, PT_SELECT_INFO_MULTI_UPDATE_AGG))
    {
      XASL_SET_FLAG (xasl, XASL_MULTI_UPDATE_AGG);
    }

  /* set flag for merge query */
  if (PT_SELECT_INFO_IS_FLAGED (select_node, PT_SELECT_INFO_IS_MERGE_QUERY))
    {
      XASL_SET_FLAG (xasl, XASL_IS_MERGE_QUERY);
      /* set flag to ignore class oid in object fetch */
      if (xasl->fptr_list)
    {
      XASL_SET_FLAG (xasl->fptr_list, XASL_OBJFETCH_IGNORE_CLASSOID);
    }
    }

  /* restore old parent xasl */
  parser->parent_proc_xasl = save_parent_proc_xasl;

  scan_check_parallel_heap_scan_possible (xasl);

  return xasl;

exit_on_error:

  /* restore old parent xasl */
  parser->parent_proc_xasl = save_parent_proc_xasl;

  return NULL;
}

/*
 * pt_to_buildvalue_proc () - Make a buildvalue xasl proc
 *   return:
 *   parser(in):
 *   select_node(in):
 *   qo_plan(in):
 */
static XASL_NODE *
pt_to_buildvalue_proc (PARSER_CONTEXT * parser, PT_NODE * select_node, QO_PLAN * qo_plan)
{
  XASL_NODE *xasl, *save_parent_proc_xasl;
  BUILDVALUE_PROC_NODE *buildvalue;
  AGGREGATE_TYPE *aggregate;
  PT_NODE *saved_current_class;
  XASL_NODE *dptr_head;

  if (!select_node || select_node->node_type != PT_SELECT || !select_node->info.query.q.select.from)
    {
      return NULL;
    }

  xasl = regu_xasl_node_alloc (BUILDVALUE_PROC);
  if (!xasl)
    {
      return NULL;
    }

  /* save parent xasl */
  save_parent_proc_xasl = parser->parent_proc_xasl;
  parser->parent_proc_xasl = xasl;

  buildvalue = &xasl->proc.buildvalue;
  xasl->next = NULL;

  /* set references of INST_NUM and ORDERBY_NUM values in parse tree */
  pt_set_numbering_node_etc (parser, select_node->info.query.q.select.list, &xasl->instnum_val, &xasl->ordbynum_val);
  pt_set_numbering_node_etc (parser, select_node->info.query.q.select.where, &xasl->instnum_val, &xasl->ordbynum_val);
  pt_set_numbering_node_etc (parser, select_node->info.query.orderby_for, &xasl->instnum_val, &xasl->ordbynum_val);

  /* assume parse tree correct, and PT_DISTINCT only other possibility */
  xasl->option = ((select_node->info.query.all_distinct == PT_ALL) ? Q_ALL : Q_DISTINCT);

  /* set 'etc' field for pseudocolumn nodes */
  pt_set_level_node_etc (parser, select_node->info.query.q.select.list, &xasl->level_val);
  pt_set_isleaf_node_etc (parser, select_node->info.query.q.select.list, &xasl->isleaf_val);
  pt_set_iscycle_node_etc (parser, select_node->info.query.q.select.list, &xasl->iscycle_val);
  pt_set_connect_by_operator_node_etc (parser, select_node->info.query.q.select.list, xasl);
  pt_set_qprior_node_etc (parser, select_node->info.query.q.select.list, xasl);
  pt_set_level_node_etc (parser, select_node->info.query.q.select.having, &xasl->level_val);
  pt_set_isleaf_node_etc (parser, select_node->info.query.q.select.having, &xasl->isleaf_val);
  pt_set_iscycle_node_etc (parser, select_node->info.query.q.select.having, &xasl->iscycle_val);
  pt_set_connect_by_operator_node_etc (parser, select_node->info.query.q.select.having, xasl);
  pt_set_qprior_node_etc (parser, select_node->info.query.q.select.having, xasl);

  aggregate = pt_to_aggregate (parser, select_node, NULL, NULL, NULL, NULL, NULL, &buildvalue->grbynum_val, qo_plan);

  /* the calls pt_to_out_list, pt_to_spec_list, and pt_to_if_pred, record information in the "symbol_info" structure
   * used by subsequent calls, and must be done first, before calculating subquery lists, etc. */
  xasl->outptr_list =
    pt_to_outlist (parser, select_node->info.query.q.select.list, &xasl->selected_upd_list, UNBOX_AS_VALUE);

  /* check if this select statement has click counter */
  if (xasl->selected_upd_list != NULL)
    {
      /* set lock timeout hint if specified */
      PT_NODE *hint_arg;
      float hint_wait_secs;

      xasl->selected_upd_list->wait_msecs = XASL_WAIT_MSECS_NOCHANGE;
      hint_arg = select_node->info.query.q.select.waitsecs_hint;
      if (select_node->info.query.q.select.hint & PT_HINT_LK_TIMEOUT && PT_IS_HINT_NODE (hint_arg))
    {
      hint_wait_secs = (float) atof (hint_arg->info.name.original);
      if (hint_wait_secs > 0)
        {
          xasl->selected_upd_list->wait_msecs = (int) (hint_wait_secs * 1000);
        }
      else
        {
          xasl->selected_upd_list->wait_msecs = (int) hint_wait_secs;
        }
    }
    }

  if (xasl->outptr_list == NULL)
    {
      goto exit_on_error;
    }

  pt_set_aptr (parser, select_node, xasl);

  if (select_node->info.query.q.select.hint & PT_HINT_PARALLEL)
    {
      xasl->parallelism = select_node->info.query.q.select.num_parallel_threads;
    }
  else
    {
      xasl->parallelism = -1;   /* auto-compute */
    }

  if (select_node->info.query.q.select.hint & PT_HINT_NO_PARALLEL_SUBQUERY)
    {
      XASL_SET_FLAG (xasl, XASL_NO_PARALLEL_SUBQUERY);
    }

  if (!qo_plan || !pt_gen_optimized_plan (parser, select_node, qo_plan, xasl))
    {
      PT_NODE *from;

      from = select_node->info.query.q.select.from;
      while (from)
    {
      if (from->info.spec.join_type != PT_JOIN_NONE)
        {
          PT_ERRORm (parser, from, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_OUTER_JOIN_OPT_FAILED);
          goto exit_on_error;
        }
      from = from->next;
    }

      if (select_node->info.query.q.select.flavor == PT_MERGE_SELECT)
    {
      xasl = pt_gen_simple_merge_plan (parser, select_node, qo_plan, xasl);
    }
      else
    {
      xasl = pt_gen_simple_plan (parser, select_node, qo_plan, xasl);
    }

      if (xasl == NULL)
    {
      goto exit_on_error;
    }
      buildvalue = &xasl->proc.buildvalue;
    }

  /* set access spec for aggregation */
  if (xasl->spec_list)
    {
      pt_set_access_spec_for_aggregation (parser, aggregate, xasl->spec_list);
    }

  /* check sampling scan */
  if (xasl->spec_list && xasl->spec_list->access == ACCESS_METHOD_SEQUENTIAL_SAMPLING_SCAN)
    {
      XASL_SET_FLAG (xasl, XASL_SAMPLING_SCAN);
    }

  /* save info for derived table size estimation */
  xasl->projected_size = 1;
  xasl->cardinality = 1.0;

  /* pred should never user the current instance for fetches either, so we turn off the current_class, if there is one. */
  saved_current_class = parser->symbols->current_class;
  parser->symbols->current_class = NULL;
  buildvalue->having_pred = pt_to_pred_expr (parser, select_node->info.query.q.select.having);
  parser->symbols->current_class = saved_current_class;

  if (xasl->scan_ptr)
    {
      dptr_head = xasl->scan_ptr;
      while (dptr_head->scan_ptr)
    {
      dptr_head = dptr_head->scan_ptr;
    }
    }
  else
    {
      dptr_head = xasl;
    }
  pt_set_dptr (parser, select_node->info.query.q.select.having, dptr_head, MATCH_ALL);

  /* union fields from BUILDVALUE_PROC_NODE - BUILDVALUE_PROC */
  buildvalue->agg_list = aggregate;

  /* this is not useful, set it to NULL. it was set by the old parser, and apparently used, but the use was apparently
   * redundant. */
  buildvalue->outarith_list = NULL;

  if (pt_false_search_condition (parser, select_node->info.query.q.select.where))
    {
      buildvalue->is_always_false = true;
    }
  else
    {
      buildvalue->is_always_false = false;
    }

  /* verify everything worked */
  if (!xasl->outptr_list || !xasl->spec_list || !xasl->val_list || pt_has_error (parser))
    {
      goto exit_on_error;
    }

  /* set CONNECT BY xasl */
  xasl = pt_set_connect_by_xasl (parser, select_node, xasl);
  if (!xasl)
    {
      goto exit_on_error;
    }

  /* convert instnum to key limit (optimization) */
  if (pt_instnum_to_key_limit (parser, qo_plan, xasl) != NO_ERROR)
    {
      goto exit_on_error;
    }

  /* convert ordbynum to key limit if we have iscan with multiple key ranges */
  if (qo_plan && qo_plan_multi_range_opt (qo_plan))
    {
      if (pt_ordbynum_to_key_limit_multiple_ranges (parser, qo_plan, xasl) != NO_ERROR)
    {
      goto exit_on_error;
    }
    }

  /* restore old parent xasl */
  parser->parent_proc_xasl = save_parent_proc_xasl;

  scan_check_parallel_heap_scan_possible (xasl);

  return xasl;

exit_on_error:

  /* restore old parent xasl */
  parser->parent_proc_xasl = save_parent_proc_xasl;

  return NULL;
}


/*
 * pt_to_union_proc () - converts a PT_NODE tree of a query
 *                   union/intersection/difference to an XASL tree
 *   return: XASL_NODE, NULL indicates error
 *   parser(in): context
 *   node(in): a query union/difference/intersection
 *   type(in): xasl PROC type
 */
static XASL_NODE *
pt_to_union_proc (PARSER_CONTEXT * parser, PT_NODE * node, PROC_TYPE type)
{
  XASL_NODE *xasl = NULL;
  XASL_NODE *left, *right = NULL;
  SORT_LIST *orderby = NULL;
  int ordbynum_flag;

  /* note that PT_UNION, PT_DIFFERENCE, and PT_INTERSECTION node types share the same node structure */
  left = (XASL_NODE *) node->info.query.q.union_.arg1->info.query.xasl;
  right = (XASL_NODE *) node->info.query.q.union_.arg2->info.query.xasl;

  /* orderby can legitimately be null */
  orderby = pt_to_orderby (parser, node->info.query.order_by, node);

  if (left && right && (orderby || !node->info.query.order_by))
    {
      /* don't allocate till everything looks ok. */
      xasl = regu_xasl_node_alloc (type);
    }

  if (xasl)
    {
      xasl->proc.union_.left = left;
      xasl->proc.union_.right = right;

      /* assume parse tree correct, and PT_DISTINCT only other possibility */
      xasl->option = (node->info.query.all_distinct == PT_ALL) ? Q_ALL : Q_DISTINCT;

      xasl->orderby_list = orderby;

      /* clear flag */
      XASL_CLEAR_FLAG (xasl, XASL_SKIP_ORDERBY_LIST);

      /* save single tuple info */
      if (node->info.query.flag.single_tuple == 1)
    {
      xasl->is_single_tuple = true;
    }

      /* set 'etc' field of PT_NODEs which belong to inst_num() and orderby_num() expression in order to use at
       * pt_make_regu_numbering() */
      pt_set_numbering_node_etc (parser, node->info.query.orderby_for, NULL, &xasl->ordbynum_val);
      ordbynum_flag = 0;
      xasl->ordbynum_pred = pt_to_pred_expr_with_arg (parser, node->info.query.orderby_for, &ordbynum_flag);

      if (ordbynum_flag & PT_PRED_ARG_ORDBYNUM_CONTINUE)
    {
      xasl->ordbynum_flag = XASL_ORDBYNUM_FLAG_SCAN_CONTINUE;
    }

      pt_set_aptr (parser, node, xasl);

      /* save info for derived table size estimation */
      switch (type)
    {
    case UNION_PROC:
      xasl->projected_size = MAX (left->projected_size, right->projected_size);
      xasl->cardinality = left->cardinality + right->cardinality;
      break;
    case DIFFERENCE_PROC:
      xasl->projected_size = left->projected_size;
      xasl->cardinality = left->cardinality;
      break;
    case INTERSECTION_PROC:
      xasl->projected_size = MAX (left->projected_size, right->projected_size);
      xasl->cardinality = MIN (left->cardinality, right->cardinality);
      break;
    default:
      break;
    }

      if (node->info.query.limit)
    {
      PT_NODE *limit;

      limit = node->info.query.limit;
      if (limit->next)
        {
          xasl->limit_offset = pt_to_regu_variable (parser, limit, UNBOX_AS_VALUE);
          limit = limit->next;
        }
      xasl->limit_row_count = pt_to_regu_variable (parser, limit, UNBOX_AS_VALUE);
    }
    }               /* end xasl */
  else
    {
      xasl = NULL;
    }

  return xasl;
}


/*
 * pt_plan_set_query () - converts a PT_NODE tree of
 *                        a query union to an XASL tree
 *   return: XASL_NODE, NULL indicates error
 *   parser(in): context
 *   node(in): a query union/difference/intersection
 *   proc_type(in): xasl PROC type
 */
static XASL_NODE *
pt_plan_set_query (PARSER_CONTEXT * parser, PT_NODE * node, PROC_TYPE proc_type)
{
  XASL_NODE *xasl;

  /* no optimization for now */
  xasl = pt_to_union_proc (parser, node, proc_type);

  return xasl;
}

/*
 * pt_plan_cte () - converts a PT_NODE tree of a CTE to an XASL tree
 * return: XASL_NODE, NULL indicates error
 * parser(in): context
 * node(in): a CTE
 * proc_type(in): xasl PROC type
 */
static XASL_NODE *
pt_plan_cte (PARSER_CONTEXT * parser, PT_NODE * node, PROC_TYPE proc_type)
{
  XASL_NODE *xasl;
  XASL_NODE *non_recursive_part_xasl = NULL, *recursive_part_xasl = NULL;
  PT_NODE *non_recursive_part, *recursive_part;

  if (node == NULL)
    {
      return NULL;
    }

  non_recursive_part = node->info.cte.non_recursive_part;
  recursive_part = node->info.cte.recursive_part;

  if (non_recursive_part == NULL)
    {
      PT_INTERNAL_ERROR (parser, "Non recursive part should be not null");
      return NULL;
    }
  non_recursive_part_xasl = (XASL_NODE *) non_recursive_part->info.query.xasl;
  /* checking false query */
  if (non_recursive_part_xasl)
    {
      non_recursive_part_xasl->sub_xasl_id = non_recursive_part->xasl_id;
      non_recursive_part_xasl->sub_host_var_count = non_recursive_part->sub_host_var_count;
      non_recursive_part_xasl->sub_host_var_index = non_recursive_part->sub_host_var_index;
    }

  if (recursive_part)
    {
      recursive_part_xasl = (XASL_NODE *) recursive_part->info.query.xasl;
    }

  xasl = regu_xasl_node_alloc (proc_type);

  if (xasl != NULL)
    {
      xasl->proc.cte.non_recursive_part = non_recursive_part_xasl;
      xasl->proc.cte.recursive_part = recursive_part_xasl;
    }

  if (recursive_part_xasl == NULL && non_recursive_part_xasl != NULL)
    {
      /* save single tuple info, cardinality, limit... from non_recursive_part */
      if (non_recursive_part->info.query.flag.single_tuple == 1)
    {
      xasl->is_single_tuple = true;
    }

      xasl->projected_size = non_recursive_part_xasl->projected_size;
      xasl->cardinality = non_recursive_part_xasl->cardinality;

      if (non_recursive_part->info.query.limit)
    {
      PT_NODE *limit;

      limit = non_recursive_part->info.query.limit;
      if (limit->next)
        {
          xasl->limit_offset = pt_to_regu_variable (parser, limit, UNBOX_AS_VALUE);
          limit = limit->next;
        }
      xasl->limit_row_count = pt_to_regu_variable (parser, limit, UNBOX_AS_VALUE);
    }
    }

  return xasl;
}

/*
 * pt_plan_schema () - Translate a schema PT_SELECT node to
 *                           a XASL buildschema proc
 *   return: XASL_NODE, NULL indicates error
 *   parser(in): context
 *   select_node(in): of PT_SELECT type
 */
static XASL_NODE *
pt_plan_schema (PARSER_CONTEXT * parser, PT_NODE * select_node)
{
  XASL_NODE *xasl = NULL;
  int level;

  if (PT_SELECT_INFO_IS_FLAGED (select_node, PT_SELECT_INFO_COLS_SCHEMA)
      || PT_SELECT_INFO_IS_FLAGED (select_node, PT_SELECT_FULL_INFO_COLS_SCHEMA))
    {
      xasl = pt_to_buildschema_proc (parser, select_node);
      if (xasl == NULL)
    {
      return NULL;
    }
    }
  else if (PT_SELECT_INFO_IS_FLAGED (select_node, PT_SELECT_INFO_IDX_SCHEMA))
    {
      xasl = pt_to_buildschema_proc (parser, select_node);
      if (xasl == NULL)
    {
      return NULL;
    }

      qo_get_optimization_param (&level, QO_PARAM_LEVEL);
      if (level & 0x200)
    {
      unsigned int save_custom;
      save_custom = parser->custom_print;
      parser->custom_print |= PT_CONVERT_RANGE;
      fprintf (db_query_get_plan_dump_fp (), "\nQuery stmt:%s\n\n%s\n\n", "",
           parser_print_tree (parser, select_node));

      parser->custom_print = save_custom;
    }
    }

  return xasl;
}


/*
 * pt_plan_query () -
 *   return: XASL_NODE, NULL indicates error
 *   parser(in): context
 *   select_node(in): of PT_SELECT type
 */
static XASL_NODE *
pt_plan_query (PARSER_CONTEXT * parser, PT_NODE * select_node)
{
  XASL_NODE *xasl;
  QO_PLAN *plan = NULL;
  PT_NODE *spec;
  int level, trace_format;
  bool hint_ignored = false;
  bool dump_plan;

  if (select_node->node_type != PT_SELECT)
    {
      return NULL;
    }

  /* Check for join, path expr, and index optimizations */
  plan = qo_optimize_query (parser, select_node);

  /* optimization fails, ignore join hint and retry optimization */
  if (!plan && select_node->info.query.q.select.hint != PT_HINT_NONE)
    {
      hint_ignored = true;

      /* init hint */
      select_node->info.query.q.select.hint = PT_HINT_NONE;
      if (select_node->info.query.q.select.leading)
    {
      parser_free_tree (parser, select_node->info.query.q.select.leading);
      select_node->info.query.q.select.leading = NULL;
    }
      if (select_node->info.query.q.select.use_nl)
    {
      parser_free_tree (parser, select_node->info.query.q.select.use_nl);
      select_node->info.query.q.select.use_nl = NULL;
    }
      if (select_node->info.query.q.select.use_idx)
    {
      parser_free_tree (parser, select_node->info.query.q.select.use_idx);
      select_node->info.query.q.select.use_idx = NULL;
    }
      if (select_node->info.query.q.select.index_ss)
    {
      parser_free_tree (parser, select_node->info.query.q.select.index_ss);
      select_node->info.query.q.select.index_ss = NULL;
    }
      if (select_node->info.query.q.select.index_ls)
    {
      parser_free_tree (parser, select_node->info.query.q.select.index_ls);
      select_node->info.query.q.select.index_ls = NULL;
    }
      if (select_node->info.query.q.select.use_merge)
    {
      parser_free_tree (parser, select_node->info.query.q.select.use_merge);
      select_node->info.query.q.select.use_merge = NULL;
    }
      if (select_node->info.query.q.select.no_use_hash)
    {
      parser_free_tree (parser, select_node->info.query.q.select.no_use_hash);
      select_node->info.query.q.select.no_use_hash = NULL;
    }
      if (select_node->info.query.q.select.use_hash)
    {
      parser_free_tree (parser, select_node->info.query.q.select.use_hash);
      select_node->info.query.q.select.use_hash = NULL;
    }

      select_node->alias_print = NULL;

#if defined(CUBRID_DEBUG)
      PT_NODE_PRINT_TO_ALIAS (parser, select_node, PT_CONVERT_RANGE);
#endif /* CUBRID_DEBUG */

      plan = qo_optimize_query (parser, select_node);
    }

  if (pt_is_single_tuple (parser, select_node))
    {
      xasl = pt_to_buildvalue_proc (parser, select_node, plan);
    }
  else
    {
      xasl = pt_to_buildlist_proc (parser, select_node, plan);
    }

  qo_get_optimization_param (&level, QO_PARAM_LEVEL);
  if (level >= 0x100 && !PT_SELECT_INFO_IS_FLAGED (select_node, PT_SELECT_INFO_COLS_SCHEMA)
      && !PT_SELECT_INFO_IS_FLAGED (select_node, PT_SELECT_FULL_INFO_COLS_SCHEMA)
      && !(select_node->info.query.q.select.hint & PT_HINT_SAMPLING_SCAN)
      && !select_node->flag.is_system_generated_stmt
      && !((spec = select_node->info.query.q.select.from) != NULL
       && spec->info.spec.derived_table_type == PT_IS_SHOWSTMT))
    {
      dump_plan = true;
    }
  else
    {
      dump_plan = false;
    }

  /* Print out any needed post-optimization info.  Leave a way to find out about environment info if we aren't able to
   * produce a plan. If this happens in the field at least we'll be able to glean some info */
  if (plan != NULL && dump_plan == true)
    {
      FILE *dump_fp = db_query_get_plan_dump_fp ();
      fputs ("\nQuery plan:\n", dump_fp);
      qo_plan_dump (plan, dump_fp);
    }

  if (dump_plan == true)
    {
      unsigned int save_custom;

      FILE *dump_fp = db_query_get_plan_dump_fp ();
      if (DETAILED_DUMP (level))
    {
      save_custom = parser->custom_print;
      parser->custom_print |= PT_CONVERT_RANGE;
      parser->custom_print |= PT_PRINT_NO_CURRENT_USER_NAME;
      fprintf (dump_fp, "\nQuery stmt:%s\n\n%s\n\n", ((hint_ignored) ? " [Warning: HINT ignored]" : ""),
           parser_print_tree (parser, select_node));
      parser->custom_print = save_custom;
    }

      if (select_node->info.query.order_by && xasl && xasl->spec_list && xasl->spec_list->indexptr
      && xasl->spec_list->indexptr->orderby_skip)
    {
      if (DETAILED_DUMP (level))
        {
          fprintf (dump_fp, "/* ---> skip ORDER BY */\n");
        }
      else if (SIMPLE_DUMP (level))
        {
          fprintf (dump_fp, " skip ORDER BY\n");
        }
    }

      if (select_node->info.query.q.select.group_by && xasl && xasl->spec_list && xasl->spec_list->indexptr
      && xasl->spec_list->indexptr->groupby_skip)
    {
      if (DETAILED_DUMP (level))
        {
          fprintf (dump_fp, "/* ---> skip GROUP BY */\n");
        }
      else if (SIMPLE_DUMP (level))
        {
          fprintf (dump_fp, " skip GROUP BY\n");
        }
    }
    }

  if (xasl != NULL && plan != NULL)
    {
      size_t plan_len, sizeloc;
      char *ptr;
      char sql_plan_empty[] = "";
      char *sql_plan = sql_plan_empty;
      COMPILE_CONTEXT *contextp = &parser->context;

      FILE *fp = port_open_memstream (&ptr, &sizeloc);
      if (fp)
    {
      qo_plan_lite_print (plan, fp, 0);
      if (select_node->info.query.order_by && xasl && xasl->spec_list && xasl->spec_list->indexptr
          && xasl->spec_list->indexptr->orderby_skip)
        {
          fprintf (fp, "\n skip ORDER BY\n");
        }

      if (select_node->info.query.q.select.group_by && xasl && xasl->spec_list && xasl->spec_list->indexptr
          && xasl->spec_list->indexptr->groupby_skip)
        {
          fprintf (fp, "\n skip GROUP BY\n");
        }

      port_close_memstream (fp, &ptr, &sizeloc);

      if (ptr)
        {
          sql_plan = pt_alloc_packing_buf ((int) sizeloc + 1);
          if (sql_plan == NULL)
        {
          goto exit;
        }

          strncpy (sql_plan, ptr, sizeloc);
          sql_plan[sizeloc] = '\0';
          free (ptr);
        }
    }

      if (sql_plan)
    {
      plan_len = strlen (sql_plan);

      if (contextp->sql_plan_alloc_size == 0)
        {
          int size = MAX (1024, (int) plan_len * 2);
          contextp->sql_plan_text = (char *) parser_alloc (parser, size);
          if (contextp->sql_plan_text == NULL)
        {
          goto exit;
        }

          contextp->sql_plan_alloc_size = size;
          contextp->sql_plan_text[0] = '\0';
        }
      else if (contextp->sql_plan_alloc_size - (int) strlen (contextp->sql_plan_text) < (long) plan_len)
        {
          char *ptr;
          int size = (contextp->sql_plan_alloc_size + (int) plan_len) * 2;

          ptr = (char *) parser_alloc (parser, size);
          if (ptr == NULL)
        {
          goto exit;
        }

          ptr[0] = '\0';
          strcpy (ptr, contextp->sql_plan_text);

          contextp->sql_plan_text = ptr;
          contextp->sql_plan_alloc_size = size;
        }

      strcat (contextp->sql_plan_text, sql_plan);
    }
    }

  if (parser->query_trace == true && !qo_need_skip_execution () && plan != NULL && xasl != NULL)
    {
      trace_format = prm_get_integer_value (PRM_ID_QUERY_TRACE_FORMAT);

      if (trace_format == QUERY_TRACE_TEXT)
    {
      qo_top_plan_print_text (parser, xasl, select_node, plan);
    }
      else if (trace_format == QUERY_TRACE_JSON)
    {
      qo_top_plan_print_json (parser, xasl, select_node, plan);
    }
    }

  if (level >= 0x100)
    {
      FILE *dump_fp = db_query_get_plan_dump_fp ();
      if (select_node->info.query.is_subquery == PT_IS_CTE_NON_REC_SUBQUERY)
    {
      fprintf (dump_fp, "\nend of non recursive part of CTE\n");
    }
      else if (select_node->info.query.is_subquery == PT_IS_CTE_REC_SUBQUERY)
    {
      fprintf (dump_fp, "\nend of CTE definition\n");
    }
    }


exit:
  if (plan != NULL)
    {
      qo_plan_discard (plan);
    }

  return xasl;
}

/*
 * pt_xasl_spec_has_dblink () - true if any scan in the xasl chain has a TARGET_DBLINK access spec
 */
static bool
pt_xasl_spec_has_dblink (XASL_NODE * xasl)
{
  XASL_NODE *scan;
  ACCESS_SPEC_TYPE *spec;

  if (xasl == NULL)
    {
      return false;
    }

  for (scan = xasl; scan != NULL; scan = scan->scan_ptr)
    {
      for (spec = scan->spec_list; spec != NULL; spec = spec->next)
    {
      if (spec->type == TARGET_DBLINK)
        {
          return true;
        }
    }
    }
  return false;
}

/*
 * parser_generate_xasl_proc () - Creates xasl proc for parse tree.
 *  Also used for direct recursion, not for subquery recursion
 *   return:
 *   parser(in):
 *   node(in): pointer to a query structure
 *   query_list(in): pointer to the generated xasl-tree
 */
static XASL_NODE *
parser_generate_xasl_proc (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * query_list)
{
  XASL_NODE *xasl = NULL;
  PT_NODE *query;

  /* we should propagate abort error from the server */
  if (!parser->flag.abort && (PT_IS_QUERY (node) || node->node_type == PT_CTE))
    {
      /* check for cached query xasl */
      for (query = query_list; query; query = query->next)
    {
      if (query->info.query.xasl && query->info.query.id == node->info.query.id)
        {
          /* found cached query xasl */
          node->info.query.xasl = query->info.query.xasl;
          node->info.query.correlation_level = query->info.query.correlation_level;

          return (XASL_NODE *) node->info.query.xasl;
        }
    }           /* for (query = ... ) */

      /* not found cached query xasl */
      switch (node->node_type)
    {
    case PT_SELECT:
      if (PT_SELECT_INFO_IS_FLAGED (node, PT_SELECT_INFO_IDX_SCHEMA)
          || ((PT_SELECT_INFO_IS_FLAGED (node, PT_SELECT_INFO_COLS_SCHEMA)
           || PT_SELECT_INFO_IS_FLAGED (node, PT_SELECT_FULL_INFO_COLS_SCHEMA))
          && node->info.query.q.select.from->info.spec.derived_table_type != PT_IS_SUBQUERY))
        {
          xasl = pt_plan_schema (parser, node);
        }
      else
        {
          xasl = pt_plan_query (parser, node);
        }
      node->info.query.xasl = xasl;
      break;

    case PT_UNION:
      xasl = pt_plan_set_query (parser, node, UNION_PROC);
      node->info.query.xasl = xasl;
      break;

    case PT_DIFFERENCE:
      xasl = pt_plan_set_query (parser, node, DIFFERENCE_PROC);
      node->info.query.xasl = xasl;
      break;

    case PT_INTERSECTION:
      xasl = pt_plan_set_query (parser, node, INTERSECTION_PROC);
      node->info.query.xasl = xasl;
      break;

    case PT_CTE:
      xasl = pt_plan_cte (parser, node, CTE_PROC);
      node->info.cte.xasl = xasl;
      break;

    default:
      if (!pt_has_error (parser))
        {
          PT_INTERNAL_ERROR (parser, "generate xasl");
        }
      /* should never get here */
      break;
    }
    }

  if (pt_has_error (parser))
    {
      xasl = NULL;      /* signal error occurred */
    }

  if (xasl)
    {
      PT_NODE *spec;

      /* Check to see if composite locking needs to be turned on. We do not do composite locking from proxies. */
      if (node->node_type == PT_SELECT && node->info.query.xasl && !READONLY_SCAN (node->info.query.scan_op_type))
    {
      spec = node->info.query.q.select.from;
      while (spec)
        {
          if (spec->info.spec.flag & (PT_SPEC_FLAG_DELETE | PT_SPEC_FLAG_UPDATE))
        {
          PT_NODE *entity_list;

          if (spec->info.spec.flat_entity_list != NULL)
            {
              entity_list = spec->info.spec.flat_entity_list;
            }
          else if (spec->info.spec.derived_table_type == PT_IS_SET_EXPR && spec->info.spec.path_entities != NULL
               && spec->info.spec.path_entities->node_type == PT_SPEC
               && spec->info.spec.path_entities->info.spec.flat_entity_list != NULL)
            {
              entity_list = spec->info.spec.path_entities->info.spec.flat_entity_list;
            }
          else
            {
              entity_list = NULL;
            }

          if (entity_list)
            {
              if ((node->info.query.upd_del_class_cnt > 1)
              || (node->info.query.upd_del_class_cnt == 1 && xasl->scan_ptr))
            {
              MOP mop = entity_list->info.name.db_object;
              if (mop && !WS_ISVID (mop))
                {
                  XASL_NODE *scan = NULL;
                  ACCESS_SPEC_TYPE *cs = NULL;

                  for (scan = xasl; scan != NULL; scan = scan->scan_ptr)
                {
                  for (cs = scan->spec_list; cs != NULL; cs = cs->next)
                    {
                      if ((cs->type == TARGET_CLASS)
                      && (OID_EQ (&ACCESS_SPEC_CLS_OID (cs), WS_REAL_OID (mop))))
                    {
                      scan->scan_op_type = node->info.query.scan_op_type;
                      break;
                    }
                    }
                  if (cs)
                    {
                      break;
                    }
                }
                }
            }
              else
            {
              xasl->scan_op_type = node->info.query.scan_op_type;
              break;
            }
            }
        }

          spec = spec->next;
        }

      xasl->upd_del_class_cnt = node->info.query.upd_del_class_cnt;
      xasl->mvcc_reev_extra_cls_cnt = node->info.query.mvcc_reev_extra_cls_cnt;
    }

      /* set as zero correlation-level; this uncorrelated subquery need to be executed at most one time */
      if ((PT_IS_QUERY (node) && node->info.query.correlation_level == 0) || node->node_type == PT_CTE)
    {
      XASL_SET_FLAG (xasl, XASL_ZERO_CORR_LEVEL);
    }

      /* correlated subquery with DBLink: rewind CCI cursor instead of re-issuing cci_execute per outer row.
       * Assumption: conn_sql is invariant across outer rows — mq_copypush never modifies conn_sql for
       * correlated terms (they are always pushed to access_pred only).
       * NOTE: if a future optimization (e.g. join push-down) places per-row host variables into
       * conn_sql, this flag must NOT be set for that case; re-bind + re-execute would be required instead. */
      if (PT_IS_QUERY (node) && node->info.query.correlation_level > 0 && pt_xasl_spec_has_dblink (xasl))
    {
      XASL_SET_FLAG (xasl, XASL_DBLINK_CURSOR_REWIND);
    }

/* BUG FIX - COMMENT OUT: DO NOT REMOVE ME FOR USE IN THE FUTURE */
#if 0
      /* cache query xasl */
      if (node->info.query.id)
    {
      query = parser_new_node (parser, node->node_type);
      query->info.query.id = node->info.query.id;
      query->info.query.xasl = node->info.query.xasl;
      query->info.query.correlation_level = node->info.query.correlation_level;

      query_list = parser_append_node (query, query_list);
    }
#endif /* 0 */
    }
  else
    {
      /* if the previous request to get a driver caused a deadlock following message would make confuse */
      if (!parser->flag.abort && !pt_has_error (parser))
    {
      PT_INTERNAL_ERROR (parser, "generate xasl");
    }
    }

  return xasl;
}

/*
 * pt_spec_to_xasl_class_oid_list () - get class OID list
 *                                     from the spec node list
 *   return:
 *   parser(in):
 *   spec(in):
 *   oid_listp(out):
 *   lock_listp(out):
 *   tcard_listp(out):
 *   nump(out):
 *   sizep(out):
 *   includes_tde_class(out):
 */
static int
pt_spec_to_xasl_class_oid_list (PARSER_CONTEXT * parser, const PT_NODE * spec, OID ** oid_listp, int **lock_listp,
                int **tcard_listp, int *nump, int *sizep, int *includes_tde_class)
{
  PT_NODE *flat = NULL;
  OID *oid = NULL, *v_oid = NULL, *o_list = NULL;
  int *lck_list = NULL;
  int *t_list = NULL;
  DB_OBJECT *class_obj = NULL;
  SM_CLASS *smclass = NULL;
  OID *oldptr = NULL;
  OID *oid_ptr = NULL;
  int index;
  int lock = (int) NULL_LOCK;
#if defined(WINDOWS)
  unsigned int o_num, o_size, prev_o_num;
#else
  size_t o_num, o_size, prev_o_num;
#endif

  if (*oid_listp == NULL || *lock_listp == NULL || *tcard_listp == NULL)
    {
      *oid_listp = (OID *) malloc (sizeof (OID) * OID_LIST_GROWTH);
      *lock_listp = (int *) malloc (sizeof (int) * OID_LIST_GROWTH);
      *tcard_listp = (int *) malloc (sizeof (int) * OID_LIST_GROWTH);
      *sizep = OID_LIST_GROWTH;
    }

  if (*oid_listp == NULL || *lock_listp == NULL || *tcard_listp == NULL || *nump >= *sizep)
    {
      goto error;
    }

  o_num = *nump;
  o_size = *sizep;
  o_list = *oid_listp;
  lck_list = *lock_listp;
  t_list = *tcard_listp;

  /* traverse spec list which is a FROM clause */
  for (; spec; spec = spec->next)
    {
      /* traverse flat entity list which are resolved classes */
      if (spec->info.spec.flag & PT_SPEC_FLAG_DELETE || spec->info.spec.flag & PT_SPEC_FLAG_UPDATE)
    {
      lock = (int) IX_LOCK;
    }
      else
    {
      lock = (int) IS_LOCK;
    }
      for (flat = spec->info.spec.flat_entity_list; flat; flat = flat->next)
    {
      /* get the OID of the class object which is fetched before */
      oid = ((flat->info.name.db_object != NULL) ? ws_identifier (flat->info.name.db_object) : NULL);
      v_oid = NULL;
      while (oid != NULL)
        {
          prev_o_num = o_num;
          oid_ptr = (OID *) lsearch (oid, o_list, &o_num, sizeof (OID), oid_compare);

          if (o_num > prev_o_num && (long) o_num > (*nump))
        {
          int is_class = 0;

          /* init #pages */
          *(t_list + o_num - 1) = XASL_CLASS_NO_TCARD;

          /* get #pages of the given class */
          class_obj = flat->info.name.db_object;

          assert (class_obj != NULL);
          assert (locator_is_class (class_obj, DB_FETCH_QUERY_READ) > 0);
          assert (!OID_ISTEMP (WS_OID (class_obj)));

          if (class_obj != NULL)
            {
              is_class = locator_is_class (class_obj, DB_FETCH_QUERY_READ);
              if (is_class < 0)
            {
              goto error;
            }
            }
          if (is_class && !OID_ISTEMP (WS_OID (class_obj)))
            {
              if (au_fetch_class (class_obj, &smclass, AU_FETCH_READ, AU_SELECT) == NO_ERROR)
            {
              if (smclass)
                {
                  if (smclass->tde_algorithm != TDE_ALGORITHM_NONE)
                {
                  *includes_tde_class = 1;
                }
                  if (smclass->stats)
                {
                  assert (smclass->stats->heap_num_pages >= 0);
                  *(t_list + o_num - 1) = smclass->stats->heap_num_pages;
                }
                }
            }
              else
            {
              /* avoid continue in loop with error */
              goto error;
            }
            }

          /* Lock for scans is IS_LOCK/IX_LOCK. */
          *(lck_list + o_num - 1) = lock;
        }
          else
        {
          /* Find index of existing object. */
          assert (oid_ptr != NULL);
          index = (int) (oid_ptr - o_list);

          /* Merge existing lock with IS_LOCK/IX_LOCK. */
          lck_list[index] = lock_conv ((LOCK) lck_list[index], (LOCK) lock);
        }

          if (o_num >= o_size)
        {
          o_size += OID_LIST_GROWTH;
          oldptr = (OID *) o_list;
          o_list = (OID *) realloc (o_list, o_size * sizeof (OID));
          if (o_list == NULL)
            {
              free_and_init (oldptr);
              *oid_listp = NULL;
              goto error;
            }

          oldptr = (OID *) lck_list;
          lck_list = (int *) realloc (lck_list, o_size * sizeof (int));
          if (lck_list == NULL)
            {
              free_and_init (oldptr);
              free_and_init (o_list);
              *oid_listp = NULL;
              *lock_listp = NULL;
              goto error;
            }

          oldptr = (OID *) t_list;
          t_list = (int *) realloc (t_list, o_size * sizeof (int));
          if (t_list == NULL)
            {
              free_and_init (oldptr);
              free_and_init (o_list);
              free_and_init (lck_list);
              *oid_listp = NULL;
              *lock_listp = NULL;
              *tcard_listp = NULL;
              goto error;
            }
        }

          if (v_oid == NULL)
        {
          /* get the OID of the view object */
          v_oid = ((flat->info.name.virt_object != NULL) ? ws_identifier (flat->info.name.virt_object) : NULL);
          oid = v_oid;
        }
          else
        {
          break;
        }
        }
    }
    }

  *nump = o_num;
  *sizep = o_size;
  *oid_listp = o_list;
  *lock_listp = lck_list;
  *tcard_listp = t_list;

  return o_num;

error:
  if (*oid_listp)
    {
      free_and_init (*oid_listp);
    }

  if (*lock_listp)
    {
      free_and_init (*lock_listp);
    }

  if (*tcard_listp)
    {
      free_and_init (*tcard_listp);
    }

  *nump = *sizep = 0;

  return -1;
}


/*
 * pt_serial_to_xasl_class_oid_list () - get serial OID list
 *                                     from the node
 *   return:
 *   parser(in):
 *   serial(in):
 *   oid_listp(out):
 *   lock_listp(out):
 *   tcard_listp(out):
 *   nump(out):
 *   sizep(out):
 */
static int
pt_serial_to_xasl_class_oid_list (PARSER_CONTEXT * parser, const PT_NODE * serial, OID ** oid_listp, int **lock_listp,
                  int **tcard_listp, int *nump, int *sizep)
{
  MOP serial_mop;
  OID *serial_oid_p;
  OID *o_list = NULL;
  int *lck_list = NULL;
  int *t_list = NULL;
  void *oldptr = NULL;
#if defined(WINDOWS)
  unsigned int o_num, o_size, prev_o_num;
#else
  size_t o_num, o_size, prev_o_num;
#endif

  assert (PT_IS_EXPR_NODE (serial) && PT_IS_SERIAL (serial->info.expr.op));

  /* get the OID of the serial object which is fetched before */
  serial_mop = pt_resolve_serial (parser, serial->info.expr.arg1);
  if (serial_mop == NULL)
    {
      goto error;
    }
  serial_oid_p = db_identifier (serial_mop);
  if (serial_oid_p == NULL)
    {
      goto error;
    }

  if (*oid_listp == NULL || *tcard_listp == NULL)
    {
      *oid_listp = (OID *) malloc (sizeof (OID) * OID_LIST_GROWTH);
      if (*oid_listp == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (OID) * OID_LIST_GROWTH);
      goto error;
    }

      *lock_listp = (int *) malloc (sizeof (int) * OID_LIST_GROWTH);
      if (*lock_listp == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (int) * OID_LIST_GROWTH);
      goto error;
    }

      *tcard_listp = (int *) malloc (sizeof (int) * OID_LIST_GROWTH);
      if (*tcard_listp == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (int) * OID_LIST_GROWTH);
      goto error;
    }
      *sizep = OID_LIST_GROWTH;
    }

  if (*nump >= *sizep)
    {
      goto error;
    }

  o_num = *nump;
  o_size = *sizep;
  o_list = *oid_listp;
  lck_list = *lock_listp;
  t_list = *tcard_listp;

  prev_o_num = o_num;
  (void) lsearch (serial_oid_p, o_list, &o_num, sizeof (OID), oid_compare);
  if (o_num > prev_o_num && o_num > (size_t) * nump)
    {
      *(t_list + o_num - 1) = XASL_SERIAL_OID_TCARD;    /* init #pages */
      *(lck_list + o_num - 1) = (int) NULL_LOCK;
    }

  if (o_num >= o_size)
    {
      o_size += OID_LIST_GROWTH;
      oldptr = (void *) o_list;
      o_list = (OID *) realloc (o_list, o_size * sizeof (OID));
      if (o_list == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, o_size * sizeof (OID));

      free_and_init (oldptr);
      *oid_listp = NULL;
      goto error;
    }

      oldptr = (void *) lck_list;
      lck_list = (int *) realloc (lck_list, o_size * sizeof (int));
      if (lck_list == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, o_size * sizeof (int));

      free_and_init (oldptr);
      free_and_init (o_list);
      *oid_listp = NULL;
      *lock_listp = NULL;
      goto error;
    }

      oldptr = (void *) t_list;
      t_list = (int *) realloc (t_list, o_size * sizeof (int));
      if (t_list == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, o_size * sizeof (int));

      free_and_init (oldptr);
      free_and_init (o_list);
      free_and_init (lck_list);
      *oid_listp = NULL;
      *lock_listp = NULL;
      *tcard_listp = NULL;
      goto error;
    }
    }

  *nump = o_num;
  *sizep = o_size;
  *oid_listp = o_list;
  *lock_listp = lck_list;
  *tcard_listp = t_list;

  return o_num;

error:
  if (*oid_listp)
    {
      free_and_init (*oid_listp);
    }

  if (*lock_listp)
    {
      free_and_init (*lock_listp);
    }

  if (*tcard_listp)
    {
      free_and_init (*tcard_listp);
    }

  *nump = *sizep = 0;

  return -1;
}

/*
 * pt_make_aptr_parent_node () - Builds a BUILDLIST proc for the query node and
 *               attaches it as the aptr to the xasl node.
 *               A list scan spec from the aptr's list file is
 *               attached to the xasl node.
 *
 * return      : XASL node.
 * parser (in) : Parser context.
 * node (in)   : Parser node containing sub-query.
 * type (in)   : XASL proc type.
 *
 * NOTE: This function should not be used in the INSERT ... VALUES case.
 */
static XASL_NODE *
pt_make_aptr_parent_node (PARSER_CONTEXT * parser, PT_NODE * node, PROC_TYPE type)
{
  XASL_NODE *aptr = NULL;
  XASL_NODE *xasl = NULL;
  REGU_VARIABLE_LIST regu_attributes;

  xasl = regu_xasl_node_alloc (type);

  if (xasl != NULL && node != NULL)
    {
      if (PT_IS_QUERY_NODE_TYPE (node->node_type))
    {
      PT_NODE *namelist;
      REGU_VARIABLE_LIST regu_var_list;

      namelist = NULL;

      aptr = parser_generate_xasl (parser, node);
      if (aptr != NULL)
        {
          XASL_CLEAR_FLAG (aptr, XASL_TOP_MOST_XASL);

          if (type == UPDATE_PROC)
        {
          PT_NODE *col;

          for (col = pt_get_select_list (parser, node); col != NULL; col = col->next)
            {
              if (PT_IS_QUERY_NODE_TYPE (col->node_type))
            {
              namelist =
                parser_append_node (pt_point_l (parser, pt_get_select_list (parser, col)), namelist);
            }
              else
            {
              namelist = parser_append_node (pt_point (parser, col), namelist);
            }
            }
        }
          else
        {
          namelist = pt_get_select_list (parser, node);
        }

          if ((type == UPDATE_PROC || type == INSERT_PROC) && aptr->outptr_list)
        {
          for (regu_var_list = aptr->outptr_list->valptrp; regu_var_list; regu_var_list = regu_var_list->next)
            {
              regu_var_list->value.flags |= REGU_VARIABLE_UPD_INS_LIST;
            }
        }

          aptr->next = NULL;
          xasl->aptr_list = aptr;

          xasl->val_list = pt_make_val_list (parser, namelist);
          if (xasl->val_list != NULL)
        {
          int *attr_offsets;

          attr_offsets = pt_make_identity_offsets (namelist);
          regu_attributes = pt_to_position_regu_variable_list (parser, namelist, xasl->val_list, attr_offsets);
          if (attr_offsets != NULL)
            {
              free_and_init (attr_offsets);
            }

          if (regu_attributes != NULL)
            {
              xasl->spec_list =
            pt_make_list_access_spec (aptr, ACCESS_METHOD_SEQUENTIAL, NULL, NULL, regu_attributes, NULL,
                          NULL, NULL);
            }
        }
          else
        {
          PT_ERRORm (parser, namelist, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
        }

          if (type == UPDATE_PROC && namelist != NULL)
        {
          parser_free_tree (parser, namelist);
        }
        }

      if (type == INSERT_PROC)
        {
          xasl->proc.insert.num_val_lists = 0;
          xasl->proc.insert.valptr_lists = NULL;
        }
    }
      else
    {
      /* Shouldn't be here */
      assert (0);
      return NULL;
    }
    }

  scan_check_parallel_heap_scan_possible (xasl);
  check_parallel_subquery_possible (xasl);

  if (pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_SEMANTIC);
      goto exit_on_error;
    }

  return xasl;

exit_on_error:

  return NULL;
}


/*
 * pt_to_constraint_pred () - Builds predicate of NOT NULL conjuncts.
 *  Then generates the corresponding filter predicate
 *   return: NO_ERROR on success, non-zero for ERROR
 *   parser(in):
 *   xasl(in): value list contains the attributes the predicate must point to
 *   spec(in): spec that generated the list file for the above value list
 *   non_null_attrs(in): list of attributes to make into a constraint pred
 *   attr_list(in): corresponds to the list file's value list positions
 *   attr_offset(in): the additional offset into the value list. This is
 *            necessary because the update prepends 2 columns for
 *            each class that will be updated on the select list of
 *            the aptr query
 *
 *   NOTE: on outer joins, the OID of a node in not_null_attrs can be null.
 *  In this case, constraint verification should be skipped, because there
 *  will be nothing to update.
 */
static int
pt_to_constraint_pred (PARSER_CONTEXT * parser, XASL_NODE * xasl, PT_NODE * spec, PT_NODE * non_null_attrs,
               PT_NODE * attr_list, int attr_offset)
{
  PT_NODE *pt_pred = NULL, *node, *conj, *next, *oid_is_null_expr, *constraint;
  PT_NODE *name, *spec_list;
  PRED_EXPR *pred = NULL;
  TABLE_INFO *ti = NULL;

  assert (xasl != NULL && spec != NULL && parser != NULL);

  node = non_null_attrs;

  parser->symbols = pt_symbol_info_alloc ();
  if (parser->symbols == NULL)
    {
      goto outofmem;
    }

  while (node != NULL)
    {
      /* we don't want a DAG so we need to NULL the next pointer as we create a conjunct for each of the
       * non_null_attrs.  Thus we must save the next pointer for the loop. */
      next = node->next;
      node->next = NULL;

      constraint = parser_new_node (parser, PT_EXPR);
      if (constraint == NULL)
    {
      goto outofmem;
    }

      oid_is_null_expr = NULL;

      name = node;
      CAST_POINTER_TO_NODE (name);
      assert (PT_IS_NAME_NODE (name));

      /* look for spec in spec list */
      spec_list = spec;
      while (spec_list != NULL)
    {
      if (spec_list->info.spec.id == name->info.name.spec_id)
        {
          break;
        }
      spec_list = spec_list->next;
    }

      assert (spec_list != NULL);

      /* create not null constraint */
      constraint->next = NULL;
      constraint->line_number = node->line_number;
      constraint->column_number = node->column_number;
      constraint->type_enum = PT_TYPE_LOGICAL;
      constraint->info.expr.op = PT_IS_NOT_NULL;
      constraint->info.expr.arg1 = node;

      if (mq_is_outer_join_spec (parser, spec_list))
    {
      /* need rewrite */
      /* verify not null constraint only if OID is not null */
      /* create OID is NULL expression */
      oid_is_null_expr = parser_new_node (parser, PT_EXPR);
      if (oid_is_null_expr == NULL)
        {
          goto outofmem;
        }
      oid_is_null_expr->type_enum = PT_TYPE_LOGICAL;
      oid_is_null_expr->info.expr.op = PT_IS_NULL;
      oid_is_null_expr->info.expr.arg1 = pt_spec_to_oid_attr (parser, spec_list, OID_NAME);
      if (oid_is_null_expr->info.expr.arg1 == NULL)
        {
          goto outofmem;
        }

      /* create an OR expression, first argument OID is NULL, second argument the constraint. This way, constraint
       * check will be skipped if OID is NULL */
      conj = parser_new_node (parser, PT_EXPR);
      if (conj == NULL)
        {
          goto outofmem;
        }
      conj->type_enum = PT_TYPE_LOGICAL;
      conj->info.expr.op = PT_OR;
      conj->info.expr.arg1 = oid_is_null_expr;
      conj->info.expr.arg2 = constraint;
    }
      else
    {
      conj = constraint;
    }
      /* add spec to table info */
      ti = pt_make_table_info (parser, spec_list);
      if (ti != NULL)
    {
      ti->next = parser->symbols->table_info;
      parser->symbols->table_info = ti;
    }

      conj->next = pt_pred;
      pt_pred = conj;
      node = next;      /* go to the next node */
    }

  parser->symbols->current_listfile = attr_list;
  parser->symbols->listfile_value_list = xasl->val_list;
  parser->symbols->listfile_attr_offset = attr_offset;

  pred = pt_to_pred_expr (parser, pt_pred);

  conj = pt_pred;
  while (conj != NULL)
    {
      conj->info.expr.arg1 = NULL;
      conj = conj->next;
    }
  if (pt_pred != NULL)
    {
      parser_free_tree (parser, pt_pred);
    }

  /* symbols are allocated with pt_alloc_packing_buf, and freed at end of xasl generation. */
  parser->symbols = NULL;

  if (xasl->type == INSERT_PROC)
    {
      xasl->proc.insert.cons_pred = pred;
    }
  else if (xasl->type == UPDATE_PROC)
    {
      xasl->proc.update.cons_pred = pred;
    }
  else
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
      return ER_GENERIC_ERROR;
    }

  return NO_ERROR;

outofmem:
  PT_ERRORm (parser, spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
  if (pt_pred != NULL)
    {
      parser_free_tree (parser, pt_pred);
    }

  return MSGCAT_RUNTIME_RESOURCES_EXHAUSTED;

}

XASL_NODE *
pt_to_xasl_for_dblink (PARSER_CONTEXT * parser, PT_NODE * spec)
{
  assert (parser != NULL && spec != NULL);

  XASL_NODE *xasl = regu_xasl_node_alloc (INSERT_PROC);
  if (xasl == NULL)
    {
      return NULL;
    }

  PT_NODE *server_spec = spec->info.spec.remote_server_name;
  PT_DBLINK_INFO *pdblink = &(server_spec->info.dblink_table);

  assert (server_spec->node_type == PT_DBLINK_TABLE_DML);
  assert (server_spec->info.dblink_table.is_name);

  if (pdblink->host_vars.count > 0)
    {
      pdblink->host_vars.index = (int *) parser_alloc (parser, pdblink->host_vars.count * sizeof (int));
      if (pdblink->host_vars.index == NULL)
    {
      assert (false);
    }

      for (int i = 0; i < pdblink->host_vars.count; i++)
    {
      pdblink->host_vars.index[i] = i;
    }
    }

  char *sql =
    (char *) (pdblink->rewritten ? pdblink->rewritten->bytes : pdblink->qstr->info.value.data_value.str->bytes);

  xasl->spec_list =
    pt_make_dblink_access_spec (ACCESS_METHOD_SEQUENTIAL, NULL, NULL, NULL,
                (char *) pdblink->url->info.value.data_value.str->bytes,
                (char *) pdblink->user->info.value.data_value.str->bytes,
                (char *) pdblink->pwd->info.value.data_value.str->bytes,
                pdblink->host_vars.count, pdblink->host_vars.index, (char *) sql);
  return xasl;
}

/*
 * pt_to_insert_xasl () - Converts an insert parse tree to an XASL tree for insert server execution.
 *
 * return     : Xasl node.
 * parser (in)    : Parser context.
 * statement (in) : Parse tree node for insert statement.
 */
XASL_NODE *
pt_to_insert_xasl (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  XASL_NODE *xasl = NULL;
  INSERT_PROC_NODE *insert = NULL;
  PT_NODE *value_clauses = NULL, *query = NULL, *val_list = NULL;
  PT_NODE *attr = NULL, *attrs = NULL;
  PT_NODE *non_null_attrs = NULL, *default_expr_attrs = NULL;
  MOBJ class_;
  OID *class_oid = NULL;
  DB_OBJECT *class_obj = NULL;
  SM_CLASS *smclass = NULL;
  HFID *hfid = NULL;
  int num_vals, num_default_expr;
  int a, i, has_uniques;
  int error = NO_ERROR;
  PT_NODE *hint_arg = NULL;
  float hint_wait_secs;

  assert (parser != NULL && statement != NULL);

  if (statement->info.insert.odku_assignments != NULL || statement->info.insert.do_replace)
    {
      statement = parser_walk_tree (parser, statement, pt_null_xasl, NULL, NULL, NULL);
    }

  if (statement->info.insert.spec && statement->info.insert.spec->info.spec.remote_server_name)
    {
      pt_check_dblink_trigger (parser, statement);
      if (statement->info.insert.spec->info.spec.remote_server_name->node_type == PT_NAME)
    {
      /*
       * pt_rewrite_dblink has to be called only once
       * at pt_rewrite_dblink
       * info.spec.remote_server_name node is changed to PT_DBLINK_DML_TABLE
       */
      pt_rewrite_for_dblink (parser, statement);
    }

      if (pt_has_error (parser))
    {
      pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, statement);
      return NULL;
    }

      return pt_to_xasl_for_dblink (parser, statement->info.insert.spec);
    }

  char *name = (char *) statement->info.insert.spec->info.spec.entity_name->info.name.original;

  has_uniques = statement->info.insert.has_uniques;
  non_null_attrs = statement->info.insert.non_null_attrs;

  value_clauses = statement->info.insert.value_clauses;
  attrs = statement->info.insert.attr_list;

  class_obj = statement->info.insert.spec->info.spec.flat_entity_list->info.name.db_object;

  class_ = locator_create_heap_if_needed (class_obj, sm_is_reuse_oid_class (class_obj));
  if (class_ == NULL)
    {
      return NULL;
    }

  hfid = sm_ch_heap (class_);
  if (hfid == NULL)
    {
      return NULL;
    }

  if (locator_flush_class (class_obj) != NO_ERROR)
    {
      return NULL;
    }

  error = pt_find_omitted_default_expr (parser, class_obj, attrs, &default_expr_attrs);
  if (error != NO_ERROR)
    {
      return NULL;
    }
  num_default_expr = pt_length_of_list (default_expr_attrs);

  if (value_clauses->info.node_list.list_type == PT_IS_SUBQUERY)
    {
      query = value_clauses->info.node_list.list;

      assert (PT_IS_QUERY (query));

      num_vals = pt_length_of_select_list (pt_get_select_list (parser, query), EXCLUDE_HIDDEN_COLUMNS);
      /* also add columns referenced in assignments */
      if (PT_IS_SELECT (query) && statement->info.insert.odku_assignments != NULL)
    {
      PT_NODE *select_list = query->info.query.q.select.list;
      PT_NODE *select_from = query->info.query.q.select.from;
      PT_NODE *assigns = statement->info.insert.odku_assignments;

      select_list = pt_append_assignment_references (parser, assigns, select_from, select_list);
      if (select_list == NULL)
        {
          PT_INTERNAL_ERROR (parser, "Error appending odku references to select list");
          return NULL;
        }
    }
    }
  else
    {
      val_list = value_clauses->info.node_list.list;
      num_vals = pt_length_of_list (val_list);
    }

  if (value_clauses->info.node_list.list_type == PT_IS_SUBQUERY)
    {
      /* for insert into ... select */
      PT_NODE *aptr_statement = value_clauses->info.node_list.list;

      xasl = pt_make_aptr_parent_node (parser, aptr_statement, INSERT_PROC);

      if (xasl != NULL && aptr_statement->info.query.flag.subquery_cached)
    {
      xasl->aptr_list->sub_xasl_id = aptr_statement->xasl_id;
      xasl->aptr_list->sub_host_var_count = aptr_statement->sub_host_var_count;
      xasl->aptr_list->sub_host_var_index = aptr_statement->sub_host_var_index;
    }
    }
  else
    {
      /* INSERT VALUES */
      int n;
      TABLE_INFO *ti;

      xasl = regu_xasl_node_alloc (INSERT_PROC);
      if (xasl == NULL)
    {
      return NULL;
    }

      pt_init_xasl_supp_info ();

      /* init parser->symbols */
      parser->symbols = pt_symbol_info_alloc ();
      ti = pt_make_table_info (parser, statement->info.insert.spec);
      if (ti == NULL)
    {
      PT_ERRORm (parser, statement->info.insert.spec, MSGCAT_SET_PARSER_RUNTIME,
             MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
      return NULL;
    }
      if (parser->symbols->table_info != NULL)
    {
      ti->next = parser->symbols->table_info;
    }
      parser->symbols->table_info = ti;

      db_query_plan_dump_fp_open ();

      value_clauses = parser_walk_tree (parser, value_clauses, parser_generate_xasl_pre, NULL,
                    parser_generate_xasl_post, &xasl_Supp_info);

      db_query_plan_dump_fp_close ();

      if ((n = xasl_Supp_info.n_oid_list) > 0 && (xasl->class_oid_list = regu_oid_array_alloc (n))
      && (xasl->class_locks = regu_int_array_alloc (n)) && (xasl->tcard_list = regu_int_array_alloc (n)))
    {
      xasl->n_oid_list = n;
      (void) memcpy (xasl->class_oid_list, xasl_Supp_info.class_oid_list, sizeof (OID) * n);
      (void) memcpy (xasl->class_locks, xasl_Supp_info.class_locks, sizeof (int) * n);
      (void) memcpy (xasl->tcard_list, xasl_Supp_info.tcard_list, sizeof (int) * n);
      if (xasl_Supp_info.includes_tde_class == 1)
        {
          XASL_SET_FLAG (xasl, XASL_INCLUDES_TDE_CLASS);
        }
    }

      pt_init_xasl_supp_info ();

      insert = &xasl->proc.insert;

      /* generate xasl->val_list */
      xasl->val_list = pt_make_val_list (parser, attrs);
      if (xasl->val_list == NULL)
    {
      return NULL;
    }

      parser->symbols->current_class = statement->info.insert.spec;
      parser->symbols->listfile_value_list = xasl->val_list;
      parser->symbols->current_listfile = statement->info.insert.attr_list;
      parser->symbols->listfile_attr_offset = 0;
      parser->symbols->listfile_unbox = UNBOX_AS_VALUE;

      /* count the number of value lists in values clause */
      for (insert->num_val_lists = 0, val_list = value_clauses; val_list != NULL;
       insert->num_val_lists++, val_list = val_list->next)
    ;

      /* alloc valptr_lists for each list of values */
      regu_array_alloc (&insert->valptr_lists, insert->num_val_lists);
      if (insert->valptr_lists == NULL)
    {
      return NULL;
    }

      for (i = 0, val_list = value_clauses; val_list != NULL; i++, val_list = val_list->next)
    {
      assert (i < insert->num_val_lists);

      if (i >= insert->num_val_lists)
        {
          PT_INTERNAL_ERROR (parser, "Generated insert xasl is corrupted: incorrect number of value lists");
        }

      insert->valptr_lists[i] = pt_to_outlist (parser, val_list->info.node_list.list, &xasl->selected_upd_list,
                           UNBOX_AS_VALUE);
      if (insert->valptr_lists[i] == NULL)
        {
          return NULL;
        }
    }
    }

  if (xasl)
    {
      if (parser->flag.return_generated_keys)
    {
      XASL_SET_FLAG (xasl, XASL_RETURN_GENERATED_KEYS);
    }

      insert = &xasl->proc.insert;
      insert->class_hfid = *hfid;

      class_oid = ws_identifier (class_obj);
      if (class_oid != NULL)
    {
      insert->class_oid = *class_oid;
    }
      else
    {
      error = ER_HEAP_UNKNOWN_OBJECT;
    }

      if (sm_partitioned_class_type (class_obj, &insert->pruning_type, NULL, NULL) != NO_ERROR)
    {
      PT_ERRORc (parser, statement, er_msg ());
      return NULL;
    }

      insert->has_uniques = has_uniques;
      insert->wait_msecs = XASL_WAIT_MSECS_NOCHANGE;

      hint_arg = statement->info.insert.waitsecs_hint;
      if (statement->info.insert.hint & PT_HINT_LK_TIMEOUT && PT_IS_HINT_NODE (hint_arg))
    {
      hint_wait_secs = (float) atof (hint_arg->info.name.original);
      if (hint_wait_secs > 0)
        {
          insert->wait_msecs = (int) (hint_wait_secs * 1000);
        }
      else
        {
          insert->wait_msecs = (int) hint_wait_secs;
        }
    }

      insert->no_logging = (statement->info.insert.hint & PT_HINT_NO_LOGGING);
      insert->do_replace = (statement->info.insert.do_replace ? 1 : 0);

      if (error >= NO_ERROR && (num_vals + num_default_expr > 0))
    {
      insert->att_id = regu_int_array_alloc (num_vals + num_default_expr);
      if (insert->att_id)
        {
          /* the identifiers of the attributes that have a default expression are placed first */
          int save_au;

          AU_DISABLE (save_au);

          for (attr = default_expr_attrs, a = 0; error >= NO_ERROR && a < num_default_expr; attr = attr->next, ++a)
        {
          insert->att_id[a] = sm_att_id (class_obj, attr->info.name.original);
          if (insert->att_id[a] < 0)
            {
              ASSERT_ERROR_AND_SET (error);
            }
        }

          for (attr = attrs, a = num_default_expr; error >= NO_ERROR && a < num_default_expr + num_vals;
           attr = attr->next, ++a)
        {
          insert->att_id[a] = sm_att_id (class_obj, attr->info.name.original);
          if (insert->att_id[a] < 0)
            {
              ASSERT_ERROR_AND_SET (error);
            }
        }

          AU_ENABLE (save_au);

          insert->vals = NULL;
          insert->num_vals = num_vals + num_default_expr;
          insert->num_default_expr = num_default_expr;
        }
      else
        {
          ASSERT_ERROR_AND_SET (error);
        }
    }
    }
  else
    {
      ASSERT_ERROR_AND_SET (error);
    }

  if (xasl != NULL && error >= NO_ERROR)
    {
      error = pt_to_constraint_pred (parser, xasl, statement->info.insert.spec, non_null_attrs, attrs, 0);
    }

  if (pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_SEMANTIC);
      xasl = NULL;
    }

  /* fill in XASL cache related information */
  if (xasl)
    {
      OID *oid;

      /* OID of the user who is creating this XASL */
      oid = ws_identifier (db_get_user ());
      if (oid != NULL)
    {
      COPY_OID (&xasl->creator_oid, oid);
    }
      else
    {
      OID_SET_NULL (&xasl->creator_oid);
    }

      /* list of class OIDs used in this XASL */
      if (xasl->aptr_list != NULL)
    {
      XASL_NODE *aptr = xasl->aptr_list;

      /*
       * in case of 'insert into foo select a from b'
       * so there is no serial oid list from values list
       */
      assert (xasl->n_oid_list == 0);

      /* reserve spec oid space by 1+ */
      xasl->class_oid_list = regu_oid_array_alloc (1 + aptr->n_oid_list);
      xasl->class_locks = regu_int_array_alloc (1 + aptr->n_oid_list);
      xasl->tcard_list = regu_int_array_alloc (1 + aptr->n_oid_list);
      if (xasl->class_oid_list == NULL || xasl->class_locks == NULL || xasl->tcard_list == NULL)
        {
          return NULL;
        }

      xasl->n_oid_list = 1 + aptr->n_oid_list;

      /* copy aptr oids to xasl */
      (void) memcpy (xasl->class_oid_list + 1, aptr->class_oid_list, sizeof (OID) * aptr->n_oid_list);
      (void) memcpy (xasl->class_locks + 1, aptr->class_locks, sizeof (int) * aptr->n_oid_list);
      (void) memcpy (xasl->tcard_list + 1, aptr->tcard_list, sizeof (int) * aptr->n_oid_list);
      XASL_SET_FLAG (xasl, aptr->flag & XASL_INCLUDES_TDE_CLASS);

      /* set spec oid */
      xasl->class_oid_list[0] = insert->class_oid;
      xasl->class_locks[0] = (int) IX_LOCK;
      xasl->tcard_list[0] = XASL_CLASS_NO_TCARD;    /* init #pages */

      xasl->dbval_cnt = aptr->dbval_cnt;
    }
      else
    {
      /* reserve spec oid space by 1+ */
      OID *o_list = regu_oid_array_alloc (1 + xasl->n_oid_list);
      int *lck_list = regu_int_array_alloc (1 + xasl->n_oid_list);
      int *t_list = regu_int_array_alloc (1 + xasl->n_oid_list);

      if (o_list == NULL || lck_list == NULL || t_list == NULL)
        {
          return NULL;
        }

      /* copy previous serial oids to new space */
      (void) memcpy (o_list + 1, xasl->class_oid_list, sizeof (OID) * xasl->n_oid_list);
      (void) memcpy (lck_list + 1, xasl->class_locks, sizeof (int) * xasl->n_oid_list);
      (void) memcpy (t_list + 1, xasl->tcard_list, sizeof (int) * xasl->n_oid_list);

      xasl->class_oid_list = o_list;
      xasl->class_locks = lck_list;
      xasl->tcard_list = t_list;

      /* set spec oid */
      xasl->n_oid_list += 1;
      xasl->class_oid_list[0] = insert->class_oid;
      xasl->class_locks[0] = (int) IX_LOCK;
      xasl->tcard_list[0] = XASL_CLASS_NO_TCARD;    /* init #pages */
    }

      assert (locator_is_class (class_obj, DB_FETCH_QUERY_READ) > 0);
      (void) au_fetch_class (class_obj, &smclass, AU_FETCH_READ, AU_SELECT);

      if (smclass)
    {
      if (smclass->tde_algorithm != TDE_ALGORITHM_NONE)
        {
          XASL_SET_FLAG (xasl, XASL_INCLUDES_TDE_CLASS);
        }
    }
    }

  if (xasl && statement->info.insert.odku_assignments)
    {
      xasl->proc.insert.odku = pt_to_odku_info (parser, statement, xasl);
      if (xasl->proc.insert.odku == NULL)
    {
      if (pt_has_error (parser))
        {
          pt_report_to_ersys (parser, PT_SEMANTIC);
        }
      return NULL;
    }
    }

  if (xasl)
    {
      xasl->query_alias = statement->alias_print;
    }

  return xasl;
}

/*
 * pt_append_assignment_references () - append names referenced in right side of ON DUPLICATE KEY UPDATE to
 *                  SELECT list of an INSERT...SELECT statement
 * return : updated node or NULL
 * parser (in)      : parser context
 * assignments (in) : assignments
 * from (in)        : SELECT spec list
 * select_list (in/out) : SELECT list
 */
static PT_NODE *
pt_append_assignment_references (PARSER_CONTEXT * parser, PT_NODE * assignments, PT_NODE * from, PT_NODE * select_list)
{
  PT_NODE *spec;
  TABLE_INFO *table_info;
  PT_NODE *ref_nodes;
  PT_NODE *save_next;
  PT_NODE *save_ref = NULL;

  if (assignments == NULL)
    {
      return select_list;
    }

  parser->symbols = pt_symbol_info_alloc ();
  if (parser->symbols == NULL)
    {
      PT_ERRORm (parser, from, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
      return NULL;
    }

  for (spec = from; spec != NULL; spec = spec->next)
    {
      save_ref = spec->info.spec.referenced_attrs;
      spec->info.spec.referenced_attrs = NULL;
      table_info = pt_make_table_info (parser, spec);
      if (table_info == NULL)
    {
      spec->info.spec.referenced_attrs = save_ref;
      return NULL;
    }

      parser->symbols->table_info = table_info;

      /* make sure we only get references from assignments, not from the spec also: call mq_get_references_helper with
       * false for the last argument */
      ref_nodes = mq_get_references_helper (parser, assignments, spec, false);

      if (pt_has_error (parser))
    {
      spec->info.spec.referenced_attrs = save_ref;
      return NULL;
    }

      while (ref_nodes != NULL)
    {
      save_next = ref_nodes->next;
      ref_nodes->next = NULL;
      if (pt_find_name (parser, ref_nodes, select_list) == NULL)
        {
          parser_append_node (ref_nodes, select_list);
        }
      ref_nodes = save_next;
    }
      spec->info.spec.referenced_attrs = save_ref;
    }

  parser->symbols = NULL;
  return select_list;
}

/*
 * pt_to_odku_info () - build a ODKU_INFO for an INSERT ... ON DUPLICATE KEY UPDATE statement
 * return : ODKU info or NULL
 * parser (in)  : parser context
 * insert (in)  : insert statement
 * xasl (in)    : INSERT XASL node
 */
static ODKU_INFO *
pt_to_odku_info (PARSER_CONTEXT * parser, PT_NODE * insert, XASL_NODE * xasl)
{
  PT_NODE *insert_spec = NULL;
  PT_NODE *select_specs = NULL;
  PT_NODE *select_list = NULL;
  PT_NODE *assignments = NULL;
  PT_NODE *prev = NULL, *node = NULL, *next = NULL, *tmp = NULL;
  PT_NODE *spec = NULL, *constraint = NULL, *save = NULL, *pt_pred = NULL;
  int insert_subquery;
  PT_ASSIGNMENTS_HELPER assignments_helper;
  DB_OBJECT *cls_obj = NULL;
  int i = 0, error = NO_ERROR;
  ODKU_INFO *odku = NULL;
  DB_VALUE *val = NULL;
  TABLE_INFO *ti = NULL;
  DB_ATTRIBUTE *attr = NULL;
  TP_DOMAIN *domain = NULL;

  assert (insert->node_type == PT_INSERT);
  assert (insert->info.insert.odku_assignments != NULL);

  parser->symbols = pt_symbol_info_alloc ();
  if (parser->symbols == NULL)
    {
      PT_ERRORm (parser, spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
      error = MSGCAT_RUNTIME_RESOURCES_EXHAUSTED;
      goto exit_on_error;
    }

  regu_alloc (odku);
  if (odku == NULL)
    {
      goto exit_on_error;
    }

  insert_spec = insert->info.insert.spec;

  insert_subquery = PT_IS_SELECT (insert->info.insert.value_clauses->info.node_list.list);

  if (insert_subquery)
    {
      select_specs = insert->info.insert.value_clauses->info.node_list.list->info.query.q.select.from;
      select_list = insert->info.insert.value_clauses->info.node_list.list->info.query.q.select.list;
    }
  else
    {
      select_list = NULL;
      select_specs = NULL;
    }

  assignments = insert->info.insert.odku_assignments;
  error = pt_append_omitted_on_update_expr_assignments (parser, assignments, insert_spec);
  if (error != NO_ERROR)
    {
      PT_INTERNAL_ERROR (parser, "odku on update insert error");
      goto exit_on_error;
    }

  /* init update attribute ids */
  odku->num_assigns = 0;
  pt_init_assignments_helper (parser, &assignments_helper, assignments);
  while (pt_get_next_assignment (&assignments_helper) != NULL)
    {
      odku->num_assigns++;
    }

  odku->attr_ids = regu_int_array_alloc (odku->num_assigns);
  if (odku->attr_ids == NULL)
    {
      goto exit_on_error;
    }

  regu_array_alloc (&odku->assignments, odku->num_assigns);
  if (odku->assignments == NULL)
    {
      goto exit_on_error;
    }

  regu_alloc (odku->attr_info);
  if (odku->attr_info == NULL)
    {
      goto exit_on_error;
    }

  /* build table info */
  ti = pt_make_table_info (parser, insert_spec);
  if (ti == NULL)
    {
      PT_ERRORm (parser, spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
      error = MSGCAT_RUNTIME_RESOURCES_EXHAUSTED;
      goto exit_on_error;
    }

  ti->next = parser->symbols->table_info;
  parser->symbols->table_info = ti;

  for (spec = select_specs; spec != NULL; spec = spec->next)
    {
      ti = pt_make_table_info (parser, spec);
      if (ti == NULL)
    {
      PT_ERRORm (parser, spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
      error = MSGCAT_RUNTIME_RESOURCES_EXHAUSTED;
      goto exit_on_error;
    }

      ti->next = parser->symbols->table_info;
      parser->symbols->table_info = ti;
    }

  /* init symbols */
  parser->symbols->current_class = insert_spec->info.spec.entity_name;
  parser->symbols->cache_attrinfo = odku->attr_info;
  parser->symbols->current_listfile = select_list;
  parser->symbols->listfile_value_list = xasl->val_list;
  parser->symbols->listfile_attr_offset = 0;

  cls_obj = insert_spec->info.spec.entity_name->info.name.db_object;

  pt_init_assignments_helper (parser, &assignments_helper, assignments);
  i = 0;
  while (pt_get_next_assignment (&assignments_helper))
    {
      attr = db_get_attribute (cls_obj, assignments_helper.lhs->info.name.original);
      if (attr == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto exit_on_error;
    }

      odku->attr_ids[i] = attr->id;
      odku->assignments[i].att_idx = i;
      odku->assignments[i].cls_idx = -1;
      if (assignments_helper.is_rhs_const)
    {
      val = pt_value_to_db (parser, assignments_helper.rhs);
      if (val == NULL)
        {
          error = ER_GENERIC_ERROR;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
          goto exit_on_error;
        }

      prev = NULL;
      for (node = insert->info.insert.odku_non_null_attrs; node != NULL; node = next)
        {
          /* Check to see if this is a NON NULL attr */
          next = node->next;

          if (!pt_name_equal (parser, node, assignments_helper.lhs))
        {
          prev = node;
          continue;
        }
          /* Found attribute in non null list. */

          if (DB_IS_NULL (val))
        {
          /* assignment of a NULL value to a non null attribute */
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_ATTRIBUTE_CANT_BE_NULL, 1,
              assignments_helper.lhs->info.name.original);
          error = ER_OBJ_ATTRIBUTE_CANT_BE_NULL;
          goto exit_on_error;
        }
          /* Break loop since we already found attribute. */
          break;
        }

      regu_alloc (odku->assignments[i].constant);
      if (odku->assignments[i].constant == NULL)
        {
          ASSERT_ERROR_AND_SET (error);
          goto exit_on_error;
        }

      domain = db_attribute_domain (attr);
      error = tp_value_cast (val, odku->assignments[i].constant, domain, false);
      if (error != DOMAIN_COMPATIBLE)
        {
          error = ER_OBJ_DOMAIN_CONFLICT;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, attr->header.name);
          goto exit_on_error;
        }
    }
      else
    {
      if (pt_is_query (assignments_helper.rhs))
        {
          XASL_NODE *rhs_xasl = NULL;

          rhs_xasl = parser_generate_xasl (parser, assignments_helper.rhs);
          if (rhs_xasl == NULL)
        {
          ASSERT_ERROR_AND_SET (error);
          goto exit_on_error;
        }
        }

      odku->assignments[i].regu_var = pt_to_regu_variable (parser, assignments_helper.rhs, UNBOX_AS_VALUE);
      if (odku->assignments[i].regu_var == NULL)
        {
          goto exit_on_error;
        }
    }

      i++;
    }

  if (insert->info.insert.odku_non_null_attrs)
    {
      /* build constraint pred */
      pt_init_assignments_helper (parser, &assignments_helper, assignments);

      node = insert->info.insert.odku_non_null_attrs;
      while (node)
    {
      save = node->next;
      CAST_POINTER_TO_NODE (node);
      do
        {
          pt_get_next_assignment (&assignments_helper);
        }
      while (assignments_helper.lhs != NULL && !pt_name_equal (parser, assignments_helper.lhs, node));

      if (assignments_helper.lhs == NULL)
        {
          /* I don't think this should happen */
          assert (false);
          break;
        }

      constraint = parser_new_node (parser, PT_EXPR);
      if (constraint == NULL)
        {
          PT_ERRORm (parser, spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
          error = MSGCAT_RUNTIME_RESOURCES_EXHAUSTED;
          goto exit_on_error;
        }

      tmp = parser_copy_tree (parser, node);
      if (tmp == NULL)
        {
          parser_free_tree (parser, constraint);
          PT_ERRORm (parser, spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
          error = MSGCAT_RUNTIME_RESOURCES_EXHAUSTED;
          goto exit_on_error;
        }

      constraint->next = pt_pred;
      constraint->line_number = node->line_number;
      constraint->column_number = node->column_number;
      constraint->info.expr.op = PT_IS_NOT_NULL;
      constraint->info.expr.arg1 = tmp;
      pt_pred = constraint;

      node = save;
    }

      odku->cons_pred = pt_to_pred_expr (parser, pt_pred);
      if (odku->cons_pred == NULL)
    {
      goto exit_on_error;
    }
    }

  if (pt_pred != NULL)
    {
      parser_free_tree (parser, pt_pred);
    }

  return odku;

exit_on_error:
  if (er_errid () == NO_ERROR && !pt_has_error (parser))
    {
      PT_INTERNAL_ERROR (parser, "ODKU Info generation failed");
      error = ER_FAILED;
    }
  if (pt_pred != NULL)
    {
      parser_free_tree (parser, pt_pred);
    }
  return NULL;
}

/*
 * pt_init_pred_expr_context () -
 *    return: error code
 *   parser(in): context
 *   predicate(in) : predicate parse tree
 *   spec(in): entity spec
 *   pred_expr(out): PRED_EXPR_WITH_CONTEXT
 */
static int
pt_init_pred_expr_context (PARSER_CONTEXT * parser, PT_NODE * predicate, PT_NODE * spec,
               PRED_EXPR_WITH_CONTEXT * pred_expr)
{
  PRED_EXPR *pred = NULL;
  PT_NODE *node_list = NULL;
  int cnt_attrs = 0;
  ATTR_ID *attrids_pred = NULL;
  REGU_VARIABLE_LIST regu_attributes_pred = NULL;
  SYMBOL_INFO *symbols = NULL;
  TABLE_INFO *table_info = NULL;
  int attr_num = 0;

  assert (pred_expr != NULL && spec != NULL && parser != NULL);

  parser->symbols = pt_symbol_info_alloc ();
  if (parser->symbols == NULL)
    {
      goto outofmem;
    }

  symbols = parser->symbols;

  symbols->table_info = pt_make_table_info (parser, spec);
  if (symbols->table_info == NULL)
    {
      goto outofmem;
    }

  table_info = symbols->table_info;
  /* should be only one node in flat_entity_list */
  symbols->current_class = spec->info.spec.flat_entity_list;
  regu_alloc (symbols->cache_attrinfo);
  if (symbols->cache_attrinfo == NULL)
    {
      goto outofmem;
    }

  table_info->class_spec = spec;
  table_info->exposed = spec->info.spec.range_var->info.name.original;
  table_info->spec_id = spec->info.spec.id;
  /* don't care about attribute_list and value_list */
  table_info->attribute_list = NULL;
  table_info->value_list = NULL;

  (void) pt_get_pred_attrs (parser, symbols->table_info, predicate, &node_list);

  regu_attributes_pred = pt_to_regu_variable_list (parser, node_list, UNBOX_AS_VALUE, NULL, NULL);
  cnt_attrs = pt_cnt_attrs (regu_attributes_pred);
  attrids_pred = regu_int_array_alloc (cnt_attrs);
  attr_num = 0;
  pt_fill_in_attrid_array (regu_attributes_pred, attrids_pred, &attr_num);

  pred = pt_to_pred_expr (parser, predicate);

  pred_expr->pred = pred;
  pred_expr->attrids_pred = attrids_pred;
  pred_expr->num_attrs_pred = cnt_attrs;
  pred_expr->cache_pred = symbols->cache_attrinfo;

  if (node_list)
    {
      parser_free_tree (parser, node_list);
    }

  /* symbols are allocated with pt_alloc_packing_buf, and freed at end of xasl generation. */
  parser->symbols = NULL;
  return NO_ERROR;

outofmem:

  if (node_list)
    {
      parser_free_tree (parser, node_list);
    }

  PT_ERRORm (parser, spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);

  return MSGCAT_RUNTIME_RESOURCES_EXHAUSTED;
}

/*
 * pt_to_pred_with_context () - Create a PRED_EXPR_WITH_CONTEXT from filter
 *              predicate parse tree
 *   return: PRED_EXPR_WITH_CONTEXT *
 *   parser(in):
 *   predicate(in): predicate parse tree
 *   spec(in): entity spec
 */
PRED_EXPR_WITH_CONTEXT *
pt_to_pred_with_context (PARSER_CONTEXT * parser, PT_NODE * predicate, PT_NODE * spec)
{
  DB_OBJECT *class_obj = NULL;
  MOBJ class_ = NULL;
  HFID *hfid = NULL;
  PRED_EXPR_WITH_CONTEXT *pred_expr = NULL;

  if (parser == NULL || predicate == NULL || spec == NULL)
    {
      return NULL;
    }

  /* flush the class, just to be sure */
  class_obj = spec->info.spec.flat_entity_list->info.name.db_object;
  class_ = locator_create_heap_if_needed (class_obj, sm_is_reuse_oid_class (class_obj));
  if (class_ == NULL)
    {
      return NULL;
    }

  hfid = sm_ch_heap (class_);
  if (hfid == NULL)
    {
      return NULL;
    }

  regu_alloc (pred_expr);
  if (pred_expr)
    {
      (void) pt_init_pred_expr_context (parser, predicate, spec, pred_expr);
    }

  if (pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_SEMANTIC);
      pred_expr = NULL;
    }

  return pred_expr;
}

/*
 * pt_copy_upddel_hints_to_select () - copy hints from delete/update statement
 *                     to select statement.
 *   return: NO_ERROR or error code.
 *   parser(in):
 *   node(in): delete/update statement that provides the hints to be
 *         copied to the select statement.
 *   select_stmt(in): select statement that will receive hints.
 *
 * Note :
 * The hints that are copied from delete/update statement to SELECT statement
 * are: ORDERED, USE_DESC_IDX, NO_COVERING_INDEX, NO_DESC_IDX, USE_NL, USE_IDX,
 *  USE_MERGE, NO_MULTI_RANGE_OPT, RECOMPILE, LEADING.
 */
int
pt_copy_upddel_hints_to_select (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * select_stmt)
{
  int err = NO_ERROR;
  PT_HINT_ENUM hint_flags =
    PT_HINT_ORDERED | PT_HINT_USE_IDX_DESC | PT_HINT_NO_COVERING_IDX | PT_HINT_NO_IDX_DESC | PT_HINT_USE_NL |
    PT_HINT_USE_IDX | PT_HINT_USE_MERGE | PT_HINT_NO_MULTI_RANGE_OPT | PT_HINT_RECOMPILE | PT_HINT_NO_SORT_LIMIT |
    PT_HINT_LEADING | PT_HINT_NO_USE_HASH | PT_HINT_USE_HASH | PT_HINT_NO_PARALLEL_HEAP_SCAN |
    PT_HINT_NO_PARALLEL_SUBQUERY | PT_HINT_NO_PARALLEL_HASH_JOIN | PT_HINT_PARALLEL;
  PT_NODE *arg = NULL;

  switch (node->node_type)
    {
    case PT_DELETE:
      hint_flags &= node->info.delete_.hint;
      break;
    case PT_UPDATE:
      hint_flags &= node->info.update.hint;
      break;
    default:
      return NO_ERROR;
    }

  select_stmt->flag.is_system_generated_stmt = node->flag.is_system_generated_stmt;

  select_stmt->info.query.q.select.hint = (PT_HINT_ENUM) (select_stmt->info.query.q.select.hint | hint_flags);
  select_stmt->flag.recompile = node->flag.recompile;

  if (hint_flags & PT_HINT_LEADING)
    {
      switch (node->node_type)
    {
    case PT_DELETE:
      arg = node->info.delete_.leading_hint;
      break;
    case PT_UPDATE:
      arg = node->info.update.leading_hint;
      break;
    default:
      break;
    }
      if (arg != NULL)
    {
      arg = parser_copy_tree_list (parser, arg);
      if (arg == NULL)
        {
          goto exit_on_error;
        }
    }
      select_stmt->info.query.q.select.leading = arg;
    }

  if (hint_flags & PT_HINT_USE_NL)
    {
      switch (node->node_type)
    {
    case PT_DELETE:
      arg = node->info.delete_.use_nl_hint;
      break;
    case PT_UPDATE:
      arg = node->info.update.use_nl_hint;
      break;
    default:
      break;
    }
      if (arg != NULL)
    {
      arg = parser_copy_tree_list (parser, arg);
      if (arg == NULL)
        {
          goto exit_on_error;
        }
    }
      select_stmt->info.query.q.select.use_nl = arg;
    }

  if (hint_flags & PT_HINT_USE_IDX)
    {
      switch (node->node_type)
    {
    case PT_DELETE:
      arg = node->info.delete_.use_idx_hint;
      break;
    case PT_UPDATE:
      arg = node->info.update.use_idx_hint;
      break;
    default:
      break;
    }
      if (arg != NULL)
    {
      arg = parser_copy_tree_list (parser, arg);
      if (arg == NULL)
        {
          goto exit_on_error;
        }
    }
      select_stmt->info.query.q.select.use_idx = arg;
    }

  if (hint_flags & PT_HINT_USE_MERGE)
    {
      switch (node->node_type)
    {
    case PT_DELETE:
      arg = node->info.delete_.use_merge_hint;
      break;
    case PT_UPDATE:
      arg = node->info.update.use_merge_hint;
      break;
    default:
      break;
    }
      if (arg != NULL)
    {
      arg = parser_copy_tree_list (parser, arg);
      if (arg == NULL)
        {
          goto exit_on_error;
        }
    }
      select_stmt->info.query.q.select.use_merge = arg;
    }

  if (hint_flags & PT_HINT_NO_USE_HASH)
    {
      switch (node->node_type)
    {
    case PT_DELETE:
      arg = node->info.delete_.no_use_hash_hint;
      break;
    case PT_UPDATE:
      arg = node->info.update.no_use_hash_hint;
      break;
    default:
      break;
    }
      if (arg != NULL)
    {
      arg = parser_copy_tree_list (parser, arg);
      if (arg == NULL)
        {
          goto exit_on_error;
        }
    }
      select_stmt->info.query.q.select.no_use_hash = arg;
    }

  if (hint_flags & PT_HINT_USE_HASH)
    {
      switch (node->node_type)
    {
    case PT_DELETE:
      arg = node->info.delete_.use_hash_hint;
      break;
    case PT_UPDATE:
      arg = node->info.update.use_hash_hint;
      break;
    default:
      break;
    }
      if (arg != NULL)
    {
      arg = parser_copy_tree_list (parser, arg);
      if (arg == NULL)
        {
          goto exit_on_error;
        }
    }
      select_stmt->info.query.q.select.use_hash = arg;
    }

  if (hint_flags & PT_HINT_PARALLEL)
    {
      switch (node->node_type)
    {
    case PT_DELETE:
      select_stmt->info.query.q.select.num_parallel_threads = node->info.delete_.num_parallel_threads;
      break;
    case PT_UPDATE:
      select_stmt->info.query.q.select.num_parallel_threads = node->info.update.num_parallel_threads;
      break;
    default:
      break;
    }
    }

  return NO_ERROR;

exit_on_error:
  if (pt_has_error (parser))
    {
      assert (er_errid () != NO_ERROR);
      err = er_errid ();
    }
  else
    {
      err = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err, 0);
    }

  return err;
}

/*
 * pt_mvcc_flag_specs_cond_reev () - flag specs that are involved in condition
 *   return: NO_ERROR or error code.
 *   parser(in):
 *   spec_list(in): List of specs that can be referenced in condition
 *   cond(in): condition expression in which specs from spec_list can be
 *         referenced
 *
 * Note :
 * The specs flagged in this function are used in MVCC condition reevaluation
 */
static int
pt_mvcc_flag_specs_cond_reev (PARSER_CONTEXT * parser, PT_NODE * spec_list, PT_NODE * cond)
{
  PT_NODE *node = NULL, *spec = NULL;
  PT_NODE *real_refs = NULL;

  if (spec_list == NULL || cond == NULL)
    {
      return NO_ERROR;
    }

  for (spec = spec_list; spec != NULL; spec = spec->next)
    {
      real_refs = spec->info.spec.referenced_attrs;
      spec->info.spec.referenced_attrs = NULL;
      node = mq_get_references (parser, cond, spec);
      if (node == NULL)
    {
      spec->info.spec.referenced_attrs = real_refs;
      continue;
    }
      spec->info.spec.flag = (PT_SPEC_FLAG) (spec->info.spec.flag | PT_SPEC_FLAG_MVCC_COND_REEV);

      spec->info.spec.referenced_attrs = real_refs;
      parser_free_tree (parser, node);
    }

  return NO_ERROR;
}

/*
 * pt_mvcc_flag_specs_assign_reev () - flag specs that are involved in
 *                     assignments
 *   return: NO_ERROR or error code.
 *   parser(in):
 *   spec_list(in): List of specs that can be involved in assignments
 *   cond(in): condition expression in which specs from spec_list can be
 *         referenced
 *
 * Note :
 * The specs flagged in this function are used in MVCC assignments reevaluation
 */
static int
pt_mvcc_flag_specs_assign_reev (PARSER_CONTEXT * parser, PT_NODE * spec_list, PT_NODE * assign_list)
{
  PT_NODE *node = NULL, *spec = NULL;
  PT_NODE *real_refs = NULL;
  PT_ASSIGNMENTS_HELPER ah;

  if (spec_list == NULL || assign_list == NULL)
    {
      return NO_ERROR;
    }

  for (spec = spec_list; spec != NULL; spec = spec->next)
    {
      pt_init_assignments_helper (parser, &ah, assign_list);

      while (pt_get_next_assignment (&ah) != NULL)
    {
      real_refs = spec->info.spec.referenced_attrs;
      spec->info.spec.referenced_attrs = NULL;
      node = mq_get_references (parser, ah.rhs, spec);
      if (node != NULL)
        {
          spec->info.spec.flag = (PT_SPEC_FLAG) (spec->info.spec.flag | PT_SPEC_FLAG_MVCC_ASSIGN_REEV);
          spec->info.spec.referenced_attrs = real_refs;
          parser_free_tree (parser, node);
          break;
        }
      spec->info.spec.referenced_attrs = real_refs;
    }
    }

  return NO_ERROR;
}

/*
 * pt_mvcc_set_spec_assign_reev_extra_indexes () - returns indexes of specs that
 *                         appear on the right side of
 *                         assignments (and not in
 *                         condition) and have a given
 *                         spec (spec_assign) on the left
 *                         side of assignments.
 *   return: count of indexes.
 *   parser(in):
 *   spec_assign(in): spec that must be on the left side of assignments
 *   spec_list(in): List of specs (FROM clause of UPDATE statement)
 *   assign_list(in): assignments
 *   indexes(in/out): a preallocated array to store indexes
 *   indexes_alloc_size(in): the allocated size of indexes array. Used for
 *               overflow checking.
 *
 * Note :
 * The indexes refers the positions of specs in the spec_list
 */
static int
pt_mvcc_set_spec_assign_reev_extra_indexes (PARSER_CONTEXT * parser, PT_NODE * spec_assign, PT_NODE * spec_list,
                        PT_NODE * assign_list, int *indexes, int indexes_alloc_size)
{
  PT_NODE *nodes_list = NULL, *spec = NULL;
  PT_NODE *real_refs = NULL;
  PT_ASSIGNMENTS_HELPER ah;
  int idx, count = 0;

  if (spec_list == NULL || assign_list == NULL)
    {
      return 0;
    }

  for (spec = spec_list, idx = 0; spec != NULL; spec = spec->next, idx++)
    {
      if ((spec->info.spec.flag & (PT_SPEC_FLAG_MVCC_COND_REEV | PT_SPEC_FLAG_MVCC_ASSIGN_REEV)) !=
      PT_SPEC_FLAG_MVCC_ASSIGN_REEV)
    {
      /* Skip specs that are not only on the right side of assignments */
      continue;
    }

      pt_init_assignments_helper (parser, &ah, assign_list);
      while (pt_get_next_assignment (&ah) != NULL)
    {
      if (ah.lhs->info.name.spec_id == spec_assign->info.spec.id)
        {
          /* we found our spec on the left side of assignment */
          real_refs = spec->info.spec.referenced_attrs;
          spec->info.spec.referenced_attrs = NULL;

          /* check whether the spec is referenced in the right side of assignment. */
          nodes_list = mq_get_references (parser, ah.rhs, spec);
          if (nodes_list != NULL)
        {
          spec->info.spec.referenced_attrs = real_refs;
          parser_free_tree (parser, nodes_list);

          assert (count < indexes_alloc_size);
          indexes[count++] = idx;
          break;
        }
          spec->info.spec.referenced_attrs = real_refs;
        }
    }
    }

  return count;
}

/*
 * pt_mvcc_prepare_upd_del_select () - prepare generated SELECT for MVCC
 *                     reevaluation
 *   return: New statement or NULL on error.
 *   parser(in):
 *   select_stmt(in): The generated SELECT statement for UPDATE/DELETE that will
 *            be prepared.
 *
 * Note :
 *  The SELECT list must already contain the OID - CLASS OID pairs for classes
 *  that will be updated.
 *  The function adds OID - CLASS OID pairs into the SELECT statement's list for
 *  classes that are referenced in conditions or asignments and aren't already
 *  flagged for UPDATE/DELETE. These OIDs will be used at reevaluation stage to
 *  load all values needed for conditions and assignments reevaluation.
 */
static PT_NODE *
pt_mvcc_prepare_upd_del_select (PARSER_CONTEXT * parser, PT_NODE * select_stmt)
{
  PT_NODE *node = NULL, *prev = NULL, *spec = NULL, *save_next = NULL;
  PT_NODE *from = NULL, *list = NULL;
  int idx = 0, upd_del_class_cnt = 0;

  if (select_stmt == NULL || select_stmt->node_type != PT_SELECT || select_stmt->info.query.upd_del_class_cnt == 0)
    {
      return select_stmt;
    }

  /* Find the insertion point in the SELECT list */
  upd_del_class_cnt = select_stmt->info.query.upd_del_class_cnt;
  assert (upd_del_class_cnt > 0);

  node = select_stmt->info.query.q.select.list;
  idx = 0;
  while (idx < upd_del_class_cnt && node != NULL)
    {
      node = node->next;
      if (node != NULL)
    {
      prev = node;
      node = node->next;
      idx++;
    }
    }

  if (idx < upd_del_class_cnt)
    {
      PT_INTERNAL_ERROR (parser, "Invalid SELECT list");
      return NULL;
    }

  from = select_stmt->info.query.q.select.from;
  list = select_stmt->info.query.q.select.list;

  /* Add pairs OID - CLASS OID to the SELECT list that are referenced in assignments and are not referenced in
   * condition */
  for (spec = from; spec != NULL; spec = spec->next)
    {
      /* Skip classes flagged for UPDATE/DELETE because they are already in SELECT list */
      if ((spec->info.spec.flag
       & (PT_SPEC_FLAG_UPDATE | PT_SPEC_FLAG_DELETE | PT_SPEC_FLAG_MVCC_COND_REEV
          | PT_SPEC_FLAG_MVCC_ASSIGN_REEV)) == PT_SPEC_FLAG_MVCC_ASSIGN_REEV)
    {
      save_next = spec->next;
      spec->next = NULL;

      select_stmt->info.query.q.select.from = spec;
      select_stmt->info.query.q.select.list = NULL;

      select_stmt = pt_add_row_classoid_name (parser, select_stmt, 1);
      assert (select_stmt != NULL);

      select_stmt = pt_add_row_oid_name (parser, select_stmt);
      assert (select_stmt != NULL);

      spec->next = save_next;
      select_stmt->info.query.q.select.list->next->next = prev->next;
      prev->next = select_stmt->info.query.q.select.list;
      prev = prev->next->next;

      select_stmt->info.query.mvcc_reev_extra_cls_cnt++;
    }
    }

  /* Add pairs OID - CLASS OID to the SELECT list that are referenced in condition */
  for (spec = from; spec != NULL; spec = spec->next)
    {
      /* Skip classes flagged for UPDATE/DELETE because they are already in SELECT list */
      if ((spec->info.spec.flag & (PT_SPEC_FLAG_UPDATE | PT_SPEC_FLAG_DELETE | PT_SPEC_FLAG_MVCC_COND_REEV)) ==
      PT_SPEC_FLAG_MVCC_COND_REEV)
    {
      save_next = spec->next;
      spec->next = NULL;

      select_stmt->info.query.q.select.from = spec;
      select_stmt->info.query.q.select.list = NULL;

      select_stmt = pt_add_row_classoid_name (parser, select_stmt, 1);
      assert (select_stmt != NULL);

      select_stmt = pt_add_row_oid_name (parser, select_stmt);
      assert (select_stmt != NULL);

      spec->next = save_next;
      select_stmt->info.query.q.select.list->next->next = prev->next;
      prev->next = select_stmt->info.query.q.select.list;
      prev = prev->next->next;

      select_stmt->info.query.mvcc_reev_extra_cls_cnt++;
    }
    }

  select_stmt->info.query.q.select.from = from;
  select_stmt->info.query.q.select.list = list;

  return select_stmt;
}

/*
 * pt_mark_spec_list_for_update_clause -- mark the spec which need be
 *                  updated/deleted with PT_SPEC_FLAG_FOR_UPDATE_CLAUSE flag
 *   return:
 *   parser(in): context
 *   statement(in): select parse tree
 *   spec_flag(in): spec flag: PT_SPEC_FLAG_UPDATE or PT_SPEC_FLAG_DELETE
 */

void
pt_mark_spec_list_for_update_clause (PARSER_CONTEXT * parser, PT_NODE * statement, PT_SPEC_FLAG spec_flag)
{
  PT_NODE *spec;

  assert (statement->node_type == PT_SELECT);

  for (spec = statement->info.query.q.select.from; spec; spec = spec->next)
    {
      if (spec->info.spec.flag & spec_flag)
    {
      spec->info.spec.flag = (PT_SPEC_FLAG) (spec->info.spec.flag | PT_SPEC_FLAG_FOR_UPDATE_CLAUSE);
    }

      if (spec->info.spec.derived_table != NULL && spec->info.spec.derived_table->node_type == PT_SELECT)
    {
      pt_mark_spec_list_for_update_clause (parser, spec->info.spec.derived_table, spec_flag);
    }
    }
}

/*
 * pt_to_upd_del_query () - Creates a query based on the given select list,
 *  from list, and where clause
 *   return: PT_NODE *, query statement or NULL if error
 *   parser(in):
 *   select_list(in):
 *   from(in):
 *   with(in):
 *   class_specs(in):
 *   where(in):
 *   using_index(in):
 *   order_by(in):
 *   orderby_for(in):
 *   server_op(in):
 *   for_update(in): true if query is used in update operation
 *
 * Note :
 * Prepends the class oid and the instance oid onto the select list for use
 * during the update or delete operation.
 * If the operation is a server side update, the prepended class oid is
 * put in the list file otherwise the class oid is a hidden column and
 * not put in the list file
 */
PT_NODE *
pt_to_upd_del_query (PARSER_CONTEXT * parser, PT_NODE * select_names, PT_NODE * select_list, PT_NODE * from,
             PT_NODE * with, PT_NODE * class_specs, PT_NODE * where, PT_NODE * using_index, PT_NODE * order_by,
             PT_NODE * orderby_for, int server_op, SCAN_OPERATION_TYPE scan_op_type)
{
  PT_NODE *statement = NULL, *from_temp = NULL, *node = NULL;
  PT_NODE *save_next = NULL, *spec = NULL;
  unsigned int save_custom;

  assert (parser != NULL);

  statement = parser_new_node (parser, PT_SELECT);
  if (statement != NULL)
    {
      statement->info.query.with = with;

      /* this is an internally built query */
      PT_SELECT_INFO_SET_FLAG (statement, PT_SELECT_INFO_IS_UPD_DEL_QUERY);

      statement->info.query.q.select.list = parser_copy_tree_list (parser, select_list);

      if (scan_op_type == S_UPDATE)
    {
      /* The system generated select was "SELECT ..., rhs1, rhs2, ... FROM table ...".
       * When two different updates set different sets of attrs, generated select was lead to one XASL entry.
       * This causes unexpected issues of reusing an XASL entry, e.g, mismatched types.
       *
       * Uses lhs of an assignment as its column alias:
       * For example, "UPDATE t SET x = ?, y = ?;" will generate "SELECT ..., ? AS x, ? AS y FROM t;".
       *
       * pt_print_select will print aliases as well as values for the system generated select queries.
       */

      PT_NODE *lhs, *rhs;

      save_custom = parser->custom_print;
      parser->custom_print |= PT_PRINT_NO_SPECIFIED_USER_NAME;
      for (rhs = statement->info.query.q.select.list, lhs = select_names;
           rhs != NULL && lhs != NULL; rhs = rhs->next, lhs = lhs->next)
        {
          rhs->alias_print = parser_print_tree (parser, lhs);
        }
      parser->custom_print = save_custom;
    }

      statement->info.query.q.select.from = parser_copy_tree_list (parser, from);
      statement->info.query.q.select.using_index = parser_copy_tree_list (parser, using_index);

      /* add in the class specs to the spec list */
      statement->info.query.q.select.from =
    parser_append_node (parser_copy_tree_list (parser, class_specs), statement->info.query.q.select.from);

      statement->info.query.q.select.where = parser_copy_tree_list (parser, where);

      if (scan_op_type == S_UPDATE && statement->info.query.q.select.from->next != NULL)
    {
      /* this is a multi-table update statement */
      for (spec = statement->info.query.q.select.from; spec; spec = spec->next)
        {
          PT_NODE *name = NULL, *val = NULL, *last_val = NULL;

          if ((spec->info.spec.flag & PT_SPEC_FLAG_UPDATE) == 0)
        {
          /* class will not be updated, nothing to do */
          continue;
        }

          if (!mq_is_outer_join_spec (parser, spec))
        {
          /* spec is not outer joined in list; no need to rewrite */
          continue;
        }

          /*
           * Class will be updated and is outer joined.
           *
           * We must rewrite all expressions that will be assigned to
           * attributes of this class as
           *
           *     IF (class_oid IS NULL, NULL, expr)
           *
           * so that expr will evaluate and/or fail only if an assignment
           * will be done.
           */

          name = select_names;
          val = statement->info.query.q.select.list;
          for (; name && val; name = name->next, val = val->next)
        {
          PT_NODE *if_expr = NULL, *isnull_expr = NULL;
          PT_NODE *bool_expr = NULL;
          PT_NODE *nv = NULL, *class_oid = NULL;
          DB_TYPE dom_type;
          DB_VALUE nv_value, *nv_valp;
          TP_DOMAIN *dom;

          assert (name->type_enum != PT_TYPE_NULL);

          if (name->info.name.spec_id != spec->info.spec.id)
            {
              /* attribute does not belong to the class */
              last_val = val;
              continue;
            }

          /* build class oid node */
          class_oid = pt_spec_to_oid_attr (parser, spec, OID_NAME);
          if (class_oid == NULL)
            {
              assert (false);
              PT_INTERNAL_ERROR (parser, "error building oid attr");
              parser_free_tree (parser, statement);
              return NULL;
            }

          /* allocate new parser nodes */
          isnull_expr = parser_new_node (parser, PT_EXPR);
          if (isnull_expr == NULL)
            {
              assert (false);
              PT_INTERNAL_ERROR (parser, "out of memory");
              parser_free_tree (parser, statement);
              return NULL;
            }

          /* (class_oid IS NULL) logical expression */
          isnull_expr->info.expr.op = PT_ISNULL;
          isnull_expr->info.expr.arg1 = class_oid;

          bool_expr = pt_convert_to_logical_expr (parser, isnull_expr, 1, 1);
          /* NULL value node */
          dom_type = pt_type_enum_to_db (name->type_enum);
          dom = tp_domain_resolve_default (dom_type);
          if (dom == NULL)
            {
              assert (false);
              PT_INTERNAL_ERROR (parser, "error building domain");
              parser_free_tree (parser, statement);
              return NULL;
            }

          if (db_value_domain_default (&nv_value, dom_type, dom->precision, dom->scale, dom->codeset,
                           dom->collation_id, &dom->enumeration) != NO_ERROR)
            {
              assert (false);
              PT_INTERNAL_ERROR (parser, "error building default val");
              parser_free_tree (parser, statement);
              return NULL;
            }
          nv = pt_dbval_to_value (parser, &nv_value);

          assert (nv->type_enum != PT_TYPE_NULL);
          assert (nv->type_enum != PT_TYPE_NONE);

          nv_valp = pt_value_to_db (parser, nv);
          if (nv_valp == NULL)
            {
              assert (false);
              PT_INTERNAL_ERROR (parser, "error building default val");
              parser_free_tree (parser, statement);
              return NULL;
            }

          /* set as NULL value */
          (void) pr_clear_value (nv_valp);

          assert (nv->type_enum != PT_TYPE_NULL);
          assert (nv->type_enum != PT_TYPE_NONE);

          /* IF expr node */
          if_expr = parser_new_node (parser, PT_EXPR);
          if (bool_expr == NULL || nv == NULL || if_expr == NULL)
            {
              /* free allocated nodes */
              if (bool_expr)
            {
              parser_free_tree (parser, bool_expr);
            }

              if (nv)
            {
              parser_free_node (parser, nv);
            }

              if (if_expr)
            {
              parser_free_node (parser, if_expr);
            }

              assert (false);
              PT_INTERNAL_ERROR (parser, "out of memory");
              parser_free_tree (parser, statement);
              return NULL;
            }

          /* IF (ISNULL(class_oid)<>0, NULL, val) expression */
          if_expr->info.expr.op = PT_IF;
          if_expr->info.expr.arg1 = bool_expr;
          if_expr->info.expr.arg2 = nv;
          if_expr->info.expr.arg3 = val;
          if_expr->type_enum = name->type_enum;
          if_expr->data_type = parser_copy_tree_list (parser, name->data_type);

          /* rebuild links */
          PT_NODE_MOVE_NUMBER_OUTERLINK (if_expr, val);
          val = if_expr;

          if (last_val != NULL)
            {
              last_val->next = val;
            }
          else
            {
              statement->info.query.q.select.list = val;
            }


          /* remember this node as previous assignment node */
          last_val = val;
        }
        }
    }

      /* add the class and instance OIDs to the select list */
      from_temp = statement->info.query.q.select.from;
      node = from;
      statement->info.query.upd_del_class_cnt = 0;
      while (node)
    {
      if (node->node_type != PT_SPEC)
        {
          assert (false);
          PT_INTERNAL_ERROR (parser, "Invalid node type");
          parser_free_tree (parser, statement);
          return NULL;
        }
      if (node->info.spec.flag & (PT_SPEC_FLAG_UPDATE | PT_SPEC_FLAG_DELETE))
        {
          save_next = node->next;
          node->next = NULL;
          statement->info.query.q.select.from = node;
          statement = pt_add_row_classoid_name (parser, statement, server_op);
          assert (statement != NULL);
          statement = pt_add_row_oid_name (parser, statement);
          assert (statement != NULL);
          node->next = save_next;

          statement->info.query.upd_del_class_cnt++;
        }
      node = node->next;
    }
      statement->info.query.q.select.from = from_temp;

      if (scan_op_type == S_UPDATE && statement->info.query.upd_del_class_cnt == 1
      && statement->info.query.q.select.from->next != NULL && !pt_has_analytic (parser, statement))
    {

      /* In case of an update of a single table joined with other tables group the result of the select by instance
       * oid of the table to be updated */

      PT_NODE *oid_node = statement->info.query.q.select.list, *group_by;

      group_by = parser_new_node (parser, PT_SORT_SPEC);
      if (group_by == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          /* leave the error code set by check_order_by, will be handled by the calling function */
          parser_free_tree (parser, statement);
          return NULL;
        }
      group_by->info.sort_spec.asc_or_desc = PT_ASC;
      save_next = oid_node->next;
      oid_node->next = NULL;
      group_by->info.sort_spec.pos_descr.dom = pt_xasl_node_to_domain (parser, oid_node);
      group_by->info.sort_spec.pos_descr.pos_no = 1;
      group_by->info.sort_spec.expr = parser_copy_tree_list (parser, oid_node);
      oid_node->next = save_next;
      statement->info.query.q.select.group_by = group_by;
      PT_SELECT_INFO_SET_FLAG (statement, PT_SELECT_INFO_MULTI_UPDATE_AGG);

      /* can't use hash aggregation for this, might mess up order */
      statement->info.query.q.select.hint =
        (PT_HINT_ENUM) (statement->info.query.q.select.hint | PT_HINT_NO_HASH_AGGREGATE);
      /* The locking at update/delete stage does not work with GROUP BY, so, we will lock at SELECT stage. */
      PT_SELECT_INFO_SET_FLAG (statement, PT_SELECT_INFO_MVCC_LOCK_NEEDED);
    }

      /* don't allow orderby_for without order_by */
      assert (!((orderby_for != NULL) && (order_by == NULL)));

      statement->info.query.order_by = parser_copy_tree_list (parser, order_by);

      if (statement->info.query.order_by != NULL)
    {
      /* translate col names into col numbers */
      if (pt_check_order_by (parser, statement) != NO_ERROR)
        {
          /* leave the error code set by check_order_by, will be handled by the calling function */
          parser_free_tree (parser, statement);
          return NULL;
        }
    }

      statement->info.query.orderby_for = parser_copy_tree_list (parser, orderby_for);

      if (statement)
    {
      statement->info.query.scan_op_type = scan_op_type;

      /* no strict oid checking for generated subquery */
      PT_SELECT_INFO_SET_FLAG (statement, PT_SELECT_INFO_NO_STRICT_OID_CHECK);
      if (!server_op)
        {
          /* When UPDATE/DELETE statement is broker-side executed we must perform locking at SELECT stage */
          PT_SELECT_INFO_SET_FLAG (statement, PT_SELECT_INFO_MVCC_LOCK_NEEDED);
        }
    }
    }

  if (PT_SELECT_INFO_IS_FLAGED (statement, PT_SELECT_INFO_MVCC_LOCK_NEEDED))
    {
      pt_mark_spec_list_for_update_clause (parser, statement, PT_SPEC_FLAG_UPDATE);
    }

  return statement;
}

/*
 * pt_to_delete_xasl () - Converts an delete parse tree to
 *                        an XASL graph for an delete
 *   return:
 *   parser(in): context
 *   statement(in): delete parse tree
 */
XASL_NODE *
pt_to_delete_xasl (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  XASL_NODE *xasl = NULL;
  DELETE_PROC_NODE *delete_ = NULL;
  UPDDEL_CLASS_INFO *class_info = NULL;
  PT_NODE *aptr_statement = NULL;
  PT_NODE *from;
  PT_NODE *where;
  PT_NODE *with;
  PT_NODE *using_index;
  PT_NODE *class_specs;
  PT_NODE *cl_name_node;
  HFID *hfid;
  OID *class_oid;
  DB_OBJECT *class_obj;
  int num_classes = 0, num_subclasses = 0, i, j, num_cond_reev_classes = 0;
  int error = NO_ERROR;
  PT_NODE *hint_arg, *node;
  float hint_wait_secs;
  bool has_partitioned = false, abort_reevaluation = false;

  assert (parser != NULL && statement != NULL);

  from = statement->info.delete_.spec;
  where = statement->info.delete_.search_cond;
  using_index = statement->info.delete_.using_index;
  class_specs = statement->info.delete_.class_specs;
  with = statement->info.delete_.with;

  if (from && from->info.spec.remote_server_name)
    {
      pt_check_dblink_trigger (parser, statement);
      if (from->info.spec.remote_server_name->node_type == PT_NAME)
    {
      /*
       * pt_rewrite_dblink has to be called only once
       * at pt_rewrite_dblink
       * info.spec.remote_server_name node is changed to PT_DBLINK_DML_TABLE
       */
      pt_rewrite_for_dblink (parser, statement);
    }

      if (pt_has_error (parser))
    {
      pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, statement);
      return NULL;
    }

      return pt_to_xasl_for_dblink (parser, from);
    }

  if (from && from->node_type == PT_SPEC && from->info.spec.range_var)
    {
      PT_NODE *select_node, *select_list = NULL;

      node = from;
      while (node != NULL && !has_partitioned)
    {
      cl_name_node = node->info.spec.flat_entity_list;

      while (cl_name_node != NULL && !has_partitioned)
        {
          error = sm_is_partitioned_class (cl_name_node->info.name.db_object);
          if (error < 0)
        {
          goto error_return;
        }
          has_partitioned = (error ? true : false);
          error = NO_ERROR;
          cl_name_node = cl_name_node->next;
        }

      node = node->next;
    }

      /* Skip reevaluation if MVCC is not enbaled or at least a class referenced in DELETE statement is partitioned.
       * The case of partitioned classes referenced in DELETE will be handled in the future */
      if (!has_partitioned)
    {
      /* Flag specs that are referenced in conditions and assignments */

      error = pt_mvcc_flag_specs_cond_reev (parser, from, where);
      if (error != NO_ERROR)
        {
          goto error_return;
        }
    }

      /* append LOB type attributes to select_list */
      node = statement->info.delete_.spec;
      while (node)
    {
      if (!(node->info.spec.flag & PT_SPEC_FLAG_DELETE))
        {
          node = node->next;
          continue;
        }

      cl_name_node = node->info.spec.flat_entity_list;
      class_obj = cl_name_node->info.name.db_object;
      if (class_obj)
        {
          DB_ATTRIBUTE *attr;
          attr = db_get_attributes (class_obj);
          while (attr)
        {
          if (attr->type->id == DB_TYPE_BLOB || attr->type->id == DB_TYPE_CLOB)
            {
              /* add lob to select list */
              select_node = pt_name (parser, attr->header.name);
              if (select_node)
            {
              select_node->info.name.spec_id = node->info.spec.id;
              select_node->type_enum = pt_db_to_type_enum (attr->type->id);

              if (attr->header.name_space == ID_SHARED_ATTRIBUTE)
                {
                  select_node->info.name.meta_class = PT_SHARED;
                }
              else if (attr->header.name_space == ID_ATTRIBUTE)
                {
                  select_node->info.name.meta_class = PT_NORMAL;
                }
              else
                {
                  assert (0);
                }

              select_list = parser_append_node (select_node, select_list);
            }
            }
          attr = db_attribute_next (attr);
        }
        }

      node = node->next;
    }

      if (((aptr_statement =
        pt_to_upd_del_query (parser, NULL, select_list, from, with, class_specs, where, using_index, NULL, NULL, 1,
                 S_DELETE)) == NULL)
      || pt_copy_upddel_hints_to_select (parser, statement, aptr_statement) != NO_ERROR
      || ((aptr_statement = mq_translate (parser, aptr_statement)) == NULL))
    {
      goto error_return;
    }

      if (aptr_statement->info.query.q.select.group_by != NULL)
    {
      /* remove reevaluation flags if we have GROUP BY because the locking will be made at SELECT stage */
      abort_reevaluation = true;
    }
      else
    {
      /* if at least one table involved in reevaluation is a derived table then abort reevaluation and force
       * locking on select */
      for (cl_name_node = aptr_statement->info.query.q.select.from; cl_name_node != NULL;
           cl_name_node = cl_name_node->next)
        {
          if (cl_name_node->info.spec.derived_table != NULL
          && (cl_name_node->info.spec.flag | PT_SPEC_FLAG_MVCC_COND_REEV))
        {
          PT_SELECT_INFO_SET_FLAG (aptr_statement, PT_SELECT_INFO_MVCC_LOCK_NEEDED);
          abort_reevaluation = true;
          break;
        }
        }
    }

      /* These two lines disable reevaluation on UPDATE. To activate it just remove them */
      PT_SELECT_INFO_SET_FLAG (aptr_statement, PT_SELECT_INFO_MVCC_LOCK_NEEDED);
      abort_reevaluation = true;

      if (abort_reevaluation)
    {
      /* In order to abort reevaluation is enough to clear reevaluation flags from all specs (from both, delete and
       * select statements) */
      for (cl_name_node = aptr_statement->info.query.q.select.from; cl_name_node != NULL;
           cl_name_node = cl_name_node->next)
        {
          cl_name_node->info.spec.flag =
        (PT_SPEC_FLAG) (cl_name_node->info.spec.flag & ~PT_SPEC_FLAG_MVCC_COND_REEV);
        }
      for (cl_name_node = from; cl_name_node != NULL; cl_name_node = cl_name_node->next)
        {
          cl_name_node->info.spec.flag =
        (PT_SPEC_FLAG) (cl_name_node->info.spec.flag & ~PT_SPEC_FLAG_MVCC_COND_REEV);
        }
    }

      /* In case of locking at select stage add flag used at SELECT ... FOR UPDATE clause to each spec from which rows
       * will be deleted. This will ensure that rows will be locked at SELECT stage. */
      if (PT_SELECT_INFO_IS_FLAGED (aptr_statement, PT_SELECT_INFO_MVCC_LOCK_NEEDED))
    {
      pt_mark_spec_list_for_update_clause (parser, aptr_statement, PT_SPEC_FLAG_DELETE);
    }

      /* Prepare generated SELECT statement for mvcc reevaluation */
      aptr_statement = pt_mvcc_prepare_upd_del_select (parser, aptr_statement);
      if (aptr_statement == NULL)
    {
      error = er_errid ();
      if (error == NO_ERROR)
        {
          error = ER_GENERIC_ERROR;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
        }
      goto error_return;
    }

      xasl = pt_make_aptr_parent_node (parser, aptr_statement, DELETE_PROC);
      if (xasl == NULL)
    {
      goto error_return;
    }

      while (select_list)
    {
      select_node = select_list;
      select_list = select_list->next;
      parser_free_node (parser, select_node);
    }
    }

  if (xasl != NULL)
    {
      PT_NODE *node;

      delete_ = &xasl->proc.delete_;

      node = statement->info.delete_.spec;
      num_classes = num_cond_reev_classes = 0;
      while (node != NULL)
    {
      if (node->info.spec.flag & PT_SPEC_FLAG_DELETE)
        {
          num_classes++;
        }
      if (node->info.spec.flag & PT_SPEC_FLAG_MVCC_COND_REEV)
        {
          ++num_cond_reev_classes;
        }
      node = node->next;
    }
      delete_->num_classes = num_classes;
      delete_->num_reev_classes = num_cond_reev_classes;
      regu_array_alloc (&delete_->classes, num_classes);
      if (delete_->classes == NULL)
    {
      goto error_return;
    }

      delete_->mvcc_reev_classes = regu_int_array_alloc (delete_->num_reev_classes);
      if (delete_->mvcc_reev_classes == NULL && delete_->num_reev_classes)
    {
      error = er_errid ();
      goto error_return;
    }

      /* we iterate through updatable classes from left to right and fill the structures from right to left because we
       * must match the order of OID's in the generated SELECT statement */
      for (i = num_classes - 1, node = statement->info.delete_.spec; i >= 0 && node != NULL; node = node->next)
    {
      bool found_lob = false;

      if (!(node->info.spec.flag & PT_SPEC_FLAG_DELETE))
        {
          /* skip classes from which we're not deleting */
          continue;
        }

      class_info = &delete_->classes[i--];

      /* setup members not needed for DELETE */
      class_info->att_id = NULL;
      class_info->num_attrs = 0;
      /* assume it always has uniques */
      class_info->has_uniques = 1;

      cl_name_node = node->info.spec.flat_entity_list;
      class_obj = cl_name_node->info.name.db_object;
      if (sm_partitioned_class_type (class_obj, &class_info->needs_pruning, NULL, NULL) != NO_ERROR)
        {
          PT_ERRORc (parser, statement, er_msg ());
          goto error_return;
        }

      num_subclasses = 0;
      while (cl_name_node)
        {
          num_subclasses++;
          cl_name_node = cl_name_node->next;
        }
      class_info->num_subclasses = num_subclasses;
      class_info->class_oid = regu_oid_array_alloc (num_subclasses);
      if (class_info->class_oid == NULL)
        {
          goto error_return;
        }
      regu_array_alloc (&class_info->class_hfid, num_subclasses);
      if (class_info->class_hfid == NULL)
        {
          goto error_return;
        }

      if (!class_info->needs_pruning)
        {
          class_info->num_lob_attrs = regu_int_array_alloc (num_subclasses);
          if (class_info->num_lob_attrs == NULL)
        {
          goto error_return;
        }
          regu_array_alloc (&class_info->lob_attr_ids, num_subclasses);
          if (class_info->lob_attr_ids == NULL)
        {
          goto error_return;
        }
        }
      else
        {
          class_info->num_lob_attrs = NULL;
          class_info->lob_attr_ids = NULL;
        }

      j = 0;
      cl_name_node = node->info.spec.flat_entity_list;
      while (cl_name_node != NULL)
        {
          class_obj = cl_name_node->info.name.db_object;
          class_oid = ws_identifier (class_obj);
          if (class_oid == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_HEAP_UNKNOWN_OBJECT, 3, 0, 0, 0);
          goto error_return;
        }
          hfid = sm_get_ch_heap (class_obj);
          if (hfid == NULL)
        {
          goto error_return;
        }
          COPY_OID (&class_info->class_oid[j], class_oid);
          HFID_COPY (&class_info->class_hfid[j], hfid);

          if (!class_info->needs_pruning)
        {
          class_info->num_lob_attrs[j] = 0;
          class_info->lob_attr_ids[j] = NULL;

          if (cl_name_node != node->info.spec.flat_entity_list)
            {
              /* lob attributes from root table are already handled */
              DB_ATTRIBUTE *attrs, *attr;

              attrs = db_get_attributes (class_obj);
              for (attr = attrs; attr; attr = (DB_ATTRIBUTE *) attr->header.next)
            {
              if ((attr->type->id == DB_TYPE_BLOB || attr->type->id == DB_TYPE_CLOB)
                  && (attr->class_mop != node->info.spec.flat_entity_list->info.name.db_object))
                {
                  /* count lob attributes that don't belong to the root table */
                  class_info->num_lob_attrs[j]++;
                  found_lob = true;
                }
            }
              if (class_info->num_lob_attrs[j] > 0)
            {
              /* some lob attributes were found, save their ids */
              int count = 0;

              class_info->lob_attr_ids[j] = regu_int_array_alloc (class_info->num_lob_attrs[j]);
              if (!class_info->lob_attr_ids[j])
                {
                  goto error_return;
                }
              for (attr = attrs; attr; attr = (DB_ATTRIBUTE *) attr->header.next)
                {
                  if ((attr->type->id == DB_TYPE_BLOB || attr->type->id == DB_TYPE_CLOB)
                  && (attr->class_mop != node->info.spec.flat_entity_list->info.name.db_object))
                {
                  class_info->lob_attr_ids[j][count++] = attr->id;
                }
                }
            }
            }
        }

          cl_name_node = cl_name_node->next;
          j++;
        }

      if (!found_lob)
        {
          /* no lob attributes were found, num_lob_attrs and lob_attr_ids can be set to NULL. this avoids keeping
           * useless information in xasl */
          class_info->num_lob_attrs = NULL;
          class_info->lob_attr_ids = NULL;
        }
    }

      hint_arg = statement->info.delete_.waitsecs_hint;
      delete_->wait_msecs = XASL_WAIT_MSECS_NOCHANGE;
      if (statement->info.delete_.hint & PT_HINT_LK_TIMEOUT && PT_IS_HINT_NODE (hint_arg))
    {
      hint_wait_secs = (float) atof (hint_arg->info.name.original);
      if (hint_wait_secs > 0)
        {
          delete_->wait_msecs = (int) (hint_wait_secs * 1000);
        }
      else
        {
          delete_->wait_msecs = (int) hint_wait_secs;
        }
    }
      delete_->no_logging = (statement->info.delete_.hint & PT_HINT_NO_LOGGING);
      delete_->no_supplemental_log = (statement->info.delete_.hint & PT_HINT_NO_SUPPLEMENTAL_LOG);
    }

  if (pt_has_error (parser) || error < 0)
    {
      pt_report_to_ersys (parser, PT_SEMANTIC);
      xasl = NULL;
    }

  /* fill in XASL cache related information */
  if (xasl)
    {
      OID *oid;

      /* prepare data for MVCC condition reevaluation. For each class used in condition reevaluation set the position
       * (index) into select list. */

      for (cl_name_node = aptr_statement->info.query.q.select.list, i = j = 0;
       cl_name_node != NULL
       && (i < (aptr_statement->info.query.upd_del_class_cnt + aptr_statement->info.query.mvcc_reev_extra_cls_cnt));
       cl_name_node = cl_name_node->next->next, i++)
    {
      node = pt_find_spec (parser, aptr_statement->info.query.q.select.from, cl_name_node);
      assert (node != NULL);
      if (PT_IS_SPEC_FLAG_SET (node, PT_SPEC_FLAG_MVCC_COND_REEV))
        {
          /* set the position in SELECT list */
          delete_->mvcc_reev_classes[j++] = i;
        }
    }

      /* OID of the user who is creating this XASL */
      if ((oid = ws_identifier (db_get_user ())) != NULL)
    {
      COPY_OID (&xasl->creator_oid, oid);
    }
      else
    {
      OID_SET_NULL (&xasl->creator_oid);
    }


      /* list of class OIDs used in this XASL */
      if (xasl->aptr_list != NULL)
    {
      xasl->n_oid_list = xasl->aptr_list->n_oid_list;
      xasl->aptr_list->n_oid_list = 0;

      xasl->class_oid_list = xasl->aptr_list->class_oid_list;
      xasl->aptr_list->class_oid_list = NULL;

      xasl->class_locks = xasl->aptr_list->class_locks;
      xasl->aptr_list->class_locks = NULL;

      xasl->tcard_list = xasl->aptr_list->tcard_list;
      xasl->aptr_list->tcard_list = NULL;

      xasl->dbval_cnt = xasl->aptr_list->dbval_cnt;

      XASL_SET_FLAG (xasl, xasl->aptr_list->flag & XASL_INCLUDES_TDE_CLASS);
      XASL_CLEAR_FLAG (xasl->aptr_list, XASL_INCLUDES_TDE_CLASS);
    }
    }
  if (xasl)
    {
      xasl->query_alias = statement->alias_print;
    }

  if (statement->info.delete_.limit)
    {
      PT_NODE *limit = statement->info.delete_.limit;

      if (limit->next)
    {
      xasl->limit_offset = pt_to_regu_variable (parser, limit, UNBOX_AS_VALUE);
      limit = limit->next;
    }
      xasl->limit_row_count = pt_to_regu_variable (parser, limit, UNBOX_AS_VALUE);
    }
  if (aptr_statement)
    {
      /* with clause should not be freed for later use in the subquery cache. */
      aptr_statement->info.query.with = NULL;
      parser_free_tree (parser, aptr_statement);
    }

  return xasl;

error_return:
  if (aptr_statement != NULL)
    {
      /* with clause should not be freed for later use in the subquery cache. */
      aptr_statement->info.query.with = NULL;
      parser_free_tree (parser, aptr_statement);
    }

  if (pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_SEMANTIC);
      xasl = NULL;
    }
  else if (error != NO_ERROR)
    {
      xasl = NULL;
    }
  return xasl;
}

/*
 * pt_has_reev_in_subquery_pre - increments subquery level and check for
 *               reevaluation spec in subquery
 *  returns: unmodified tree
 *  parser(in): parser context
 *  tree(in): tree that can be a subquery
 *  arg(in/out): a pointer to an integer which represents the subquery level
 *  continue_walk(in/out): walk type
 *
 *  Note: used by pt_has_reev_in_subquery
 */
static PT_NODE *
pt_has_reev_in_subquery_pre (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
  int level = *(int *) arg;

  if (level < 0)
    {
      return tree;
    }

  if (PT_IS_QUERY (tree))
    {
      level++;
    }
  else if (tree->node_type == PT_SPEC
       && (tree->info.spec.flag | PT_SPEC_FLAG_MVCC_COND_REEV | PT_SPEC_FLAG_MVCC_ASSIGN_REEV) && level > 1)
    {
      level = -1;
      *continue_walk = PT_STOP_WALK;
    }

  return tree;
}

/*
 * pt_has_reev_in_subquery_post - decrements subquery level
 *  returns: unmodified tree
 *  parser(in): parser context
 *  tree(in): tree that can be a subquery
 *  arg(in/out): a pointer to an integer which represents the subquery level
 *  continue_walk(in/out): walk type
 *
 *  Note: used by pt_has_reev_in_subquery
 */
static PT_NODE *
pt_has_reev_in_subquery_post (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
  int level = *(int *) arg;

  if (level < 0)
    {
      return tree;
    }

  if (PT_IS_QUERY (tree))
    {
      level--;
    }

  return tree;
}

/*
 * pt_has_reev_in_subquery () - Checks if the statement has a subquery with
 *              specs involved in reevaluation
 *   return:
 *   parser(in): context
 *   statement(in): statement to be checked
 */
static bool
pt_has_reev_in_subquery (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int level = 0;

  (void) parser_walk_tree (parser, statement, pt_has_reev_in_subquery_pre, &level, pt_has_reev_in_subquery_post,
               &level);
  if (level < 0)
    {
      return true;
    }

  return false;
}

static PT_NODE *
pt_check_dblink_trigger_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE *prev = NULL, *values, *list;
  DB_VALUE tmp;

  if (node == NULL)
    {
      return NULL;
    }

  if (node->node_type == PT_NODE_LIST && node->info.node_list.list_type == PT_IS_VALUE)
    {
      for (list = node->info.node_list.list; list; list = list->next)
    {
      if (list->node_type == PT_NAME && list->info.name.meta_class == PT_TRIGGER_OID)
        {
          pt_evaluate_tree (parser, list, &tmp, 1);
          values = pt_dbval_to_value (parser, &tmp);
          db_value_clear (&tmp);
          if (prev == NULL)
        {
          node->info.node_list.list = values;
        }
          else
        {
          prev->next = values;
        }
          values->next = list->next;
          prev = values;
        }
      else
        {
          prev = list;
        }
    }
    }
  else if (node->node_type == PT_NAME && node->info.name.meta_class == PT_TRIGGER_OID)
    {
      pt_evaluate_tree (parser, node, &tmp, 1);
      node = pt_dbval_to_value (parser, &tmp);
      db_value_clear (&tmp);
    }

  return node;
}

static void
pt_check_dblink_trigger (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  switch (statement->node_type)
    {
    case PT_INSERT:
    case PT_UPDATE:
    case PT_DELETE:
      statement = parser_walk_tree (parser, statement, pt_check_dblink_trigger_pre, NULL, NULL, NULL);
      break;
    default:
      break;
    }

  return;
}

/*
 * pt_to_update_xasl () - Converts an update parse tree to
 *            an XASL graph for an update
 *   return:
 *   parser(in): context
 *   statement(in): update parse tree
 *   non_null_attrs(in):
 */
XASL_NODE *
pt_to_update_xasl (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE ** non_null_attrs)
{
  XASL_NODE *xasl = NULL;
  UPDATE_PROC_NODE *update = NULL;
  UPDDEL_CLASS_INFO *upd_cls = NULL;
  PT_NODE *assigns = statement->info.update.assignment;
  PT_NODE *aptr_statement = NULL;
  PT_NODE *p = NULL;
  PT_NODE *cl_name_node = NULL;
  int num_classes = 0, num_subclasses = 0, num_assign_reev_classes = 0;
  int num_cond_reev_classes = 0;
  PT_NODE *from = NULL;
  PT_NODE *where = NULL;
  PT_NODE *with = NULL;
  PT_NODE *using_index = NULL;
  PT_NODE *class_specs = NULL;
  int cl = 0, cls_idx = 0, num_vals = 0, num_consts = 0;
  int error = NO_ERROR;
  int a = 0, assign_idx = 0;
  PT_NODE *att_name_node = NULL;
  DB_VALUE *val = NULL;
  DB_ATTRIBUTE *attr = NULL;
  DB_DOMAIN *dom = NULL;
  TP_DOMAIN_STATUS dom_status;
  OID *class_oid = NULL;
  DB_OBJECT *class_obj = NULL;
  HFID *hfid = NULL;
  PT_NODE *hint_arg = NULL;
  PT_NODE *order_by = NULL;
  PT_NODE *orderby_for = NULL;
  PT_ASSIGNMENTS_HELPER assign_helper;
  PT_NODE **links = NULL;
  UPDATE_ASSIGNMENT *assign = NULL;
  PT_NODE *select_names = NULL;
  PT_NODE *select_values = NULL;
  PT_NODE *const_names = NULL;
  PT_NODE *const_values = NULL;
  OID *oid = NULL;
  float hint_wait_secs;
  int *mvcc_assign_extra_classes = NULL;
  bool has_partitioned = false, abort_reevaluation = false;


  assert (parser != NULL && statement != NULL);

  from = statement->info.update.spec;
  where = statement->info.update.search_cond;
  using_index = statement->info.update.using_index;
  class_specs = statement->info.update.class_specs;
  order_by = statement->info.update.order_by;
  orderby_for = statement->info.update.orderby_for;
  with = statement->info.update.with;

  if (from && from->info.spec.remote_server_name)
    {
      pt_check_dblink_trigger (parser, statement);
      if (from->info.spec.remote_server_name->node_type == PT_NAME)
    {
      /*
       * pt_rewrite_dblink has to be called only once
       * at pt_rewrite_dblink
       * info.spec.remote_server_name node is changed to PT_DBLINK_DML_TABLE
       */
      pt_rewrite_for_dblink (parser, statement);
    }

      if (pt_has_error (parser))
    {
      pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, statement);
      return NULL;
    }

      return pt_to_xasl_for_dblink (parser, from);
    }

  /* flush all classes */
  p = from;
  while (p != NULL && !has_partitioned)
    {
      cl_name_node = p->info.spec.flat_entity_list;

      while (cl_name_node != NULL && !has_partitioned)
    {
      error = locator_flush_class (cl_name_node->info.name.db_object);
      if (error != NO_ERROR)
        {
          goto cleanup;
        }
      error = sm_is_partitioned_class (cl_name_node->info.name.db_object);
      if (error < 0)
        {
          goto cleanup;
        }
      has_partitioned = (error ? true : false);
      error = NO_ERROR;
      cl_name_node = cl_name_node->next;
    }

      p = p->next;
    }

  if (from == NULL || from->node_type != PT_SPEC || from->info.spec.range_var == NULL)
    {
      PT_INTERNAL_ERROR (parser, "update");
      goto cleanup;
    }

  error = pt_append_omitted_on_update_expr_assignments (parser, assigns, from);
  if (error != NO_ERROR)
    {
      PT_INTERNAL_ERROR (parser, "update");
      goto cleanup;
    }

  /* Skip reevaluation if MVCC is not enbaled or at least a class referenced in UPDATE statement is partitioned. The
   * case of partitioned classes referenced in UPDATE will be handled in future */
  if (!has_partitioned)
    {
      /* Flag specs that are referenced in conditions and assignments. This must be done before the generation of
       * select statement, otherwise it will be difficult to flag specs from select statement */

      error = pt_mvcc_flag_specs_cond_reev (parser, from, where);
      if (error != NO_ERROR)
    {
      goto cleanup;
    }
      error = pt_mvcc_flag_specs_assign_reev (parser, from, statement->info.update.assignment);
      if (error != NO_ERROR)
    {
      goto cleanup;
    }
    }

  /* get assignments lists for select statement generation */
  error =
    pt_get_assignment_lists (parser, &select_names, &select_values, &const_names, &const_values, &num_vals, &num_consts,
                 statement->info.update.assignment, &links);
  if (error != NO_ERROR)
    {
      PT_INTERNAL_ERROR (parser, "update");
      goto cleanup;
    }

  aptr_statement =
    pt_to_upd_del_query (parser, select_names, select_values, from, with, class_specs, where, using_index, order_by,
             orderby_for, 1, S_UPDATE);
  /* restore assignment list here because we need to iterate through assignments later */
  pt_restore_assignment_links (statement->info.update.assignment, links, -1);

  if (aptr_statement == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  error = pt_copy_upddel_hints_to_select (parser, statement, aptr_statement);
  if (error != NO_ERROR)
    {
      goto cleanup;
    }

  aptr_statement = mq_translate (parser, aptr_statement);
  if (aptr_statement == NULL)
    {
      if (pt_has_error (parser))
    {
      pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, aptr_statement);
    }
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  if (aptr_statement->info.query.q.select.group_by != NULL)
    {
      /* remove reevaluation flags if we have GROUP BY because the locking will be made at SELECT stage */
      abort_reevaluation = true;
    }
  else if (has_partitioned || pt_has_reev_in_subquery (parser, aptr_statement))
    {
      /* if we have at least one class partitioned then perform locking at SELECT stage */
      PT_SELECT_INFO_SET_FLAG (aptr_statement, PT_SELECT_INFO_MVCC_LOCK_NEEDED);
      abort_reevaluation = true;
    }
  else
    {
      /* if at least one table involved in reevaluation is a derived table then abort reevaluation and force locking on
       * select */
      for (p = aptr_statement->info.query.q.select.from; p != NULL; p = p->next)
    {
      if (p->info.spec.derived_table != NULL
          && (p->info.spec.flag | PT_SPEC_FLAG_MVCC_COND_REEV | PT_SPEC_FLAG_MVCC_ASSIGN_REEV))
        {
          PT_SELECT_INFO_SET_FLAG (aptr_statement, PT_SELECT_INFO_MVCC_LOCK_NEEDED);
          abort_reevaluation = true;
          break;
        }
    }
    }

  /* These two lines disable reevaluation on UPDATE. To activate it just remove them */
  PT_SELECT_INFO_SET_FLAG (aptr_statement, PT_SELECT_INFO_MVCC_LOCK_NEEDED);
  abort_reevaluation = true;

  if (abort_reevaluation)
    {
      /* In order to abort reevaluation is enough to clear reevaluation flags from all specs (from both, update and
       * select statements) */
      for (p = aptr_statement->info.query.q.select.from; p != NULL; p = p->next)
    {
      p->info.spec.flag =
        (PT_SPEC_FLAG) (p->info.spec.flag & ~(PT_SPEC_FLAG_MVCC_COND_REEV | PT_SPEC_FLAG_MVCC_ASSIGN_REEV));
    }
      for (p = from; p != NULL; p = p->next)
    {
      p->info.spec.flag =
        (PT_SPEC_FLAG) (p->info.spec.flag & ~(PT_SPEC_FLAG_MVCC_COND_REEV | PT_SPEC_FLAG_MVCC_ASSIGN_REEV));
    }
    }

  /* In case of locking at select stage add flag used at SELECT ... FOR UPDATE clause to each spec from which rows will
   * be updated. This will ensure that rows will be locked at SELECT stage. */
  if (PT_SELECT_INFO_IS_FLAGED (aptr_statement, PT_SELECT_INFO_MVCC_LOCK_NEEDED))
    {
      pt_mark_spec_list_for_update_clause (parser, aptr_statement, PT_SPEC_FLAG_UPDATE);
    }

  /* Prepare generated SELECT statement for mvcc reevaluation */
  aptr_statement = pt_mvcc_prepare_upd_del_select (parser, aptr_statement);
  if (aptr_statement == NULL)
    {
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  xasl = pt_make_aptr_parent_node (parser, aptr_statement, UPDATE_PROC);
  if (xasl == NULL || xasl->aptr_list == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  /* flush all classes and count classes for update */
  num_classes = num_cond_reev_classes = num_assign_reev_classes = 0;
  p = from;
  while (p != NULL)
    {
      if (p->info.spec.flag & PT_SPEC_FLAG_UPDATE)
    {
      ++num_classes;
    }
      if (p->info.spec.flag & PT_SPEC_FLAG_MVCC_COND_REEV)
    {
      ++num_cond_reev_classes;
    }
      if ((p->info.spec.flag & (PT_SPEC_FLAG_MVCC_COND_REEV | PT_SPEC_FLAG_MVCC_ASSIGN_REEV)) ==
      PT_SPEC_FLAG_MVCC_ASSIGN_REEV)
    {
      ++num_assign_reev_classes;
    }

      cl_name_node = p->info.spec.flat_entity_list;
      while (cl_name_node != NULL)
    {
      error = locator_flush_class (cl_name_node->info.name.db_object);
      if (error != NO_ERROR)
        {
          goto cleanup;
        }
      cl_name_node = cl_name_node->next;
    }
      p = p->next;
    }

  update = &xasl->proc.update;

  update->num_classes = num_classes;
  update->num_assigns = num_vals;
  update->num_reev_classes = num_cond_reev_classes + num_assign_reev_classes;

  regu_array_alloc (&update->classes, num_classes);
  if (update->classes == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto cleanup;
    }

  regu_array_alloc (&update->assigns, update->num_assigns);
  if (update->assigns == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto cleanup;
    }

  update->mvcc_reev_classes = regu_int_array_alloc (update->num_reev_classes);
  if (update->mvcc_reev_classes == NULL && update->num_reev_classes)
    {
      error = er_errid ();
      goto cleanup;
    }

  if (num_assign_reev_classes > 0)
    {
      mvcc_assign_extra_classes = regu_int_array_alloc (num_assign_reev_classes);
      if (mvcc_assign_extra_classes == NULL)
    {
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto cleanup;
    }
    }
  /* we iterate through updatable classes from left to right and fill the structures from right to left because we must
   * match the order of OID's in the generated SELECT statement */
  for (p = from, cls_idx = num_classes - 1; cls_idx >= 0 && error == NO_ERROR; p = p->next)
    {
      /* ignore, this class will not be updated */
      if (!(p->info.spec.flag & PT_SPEC_FLAG_UPDATE))
    {
      continue;
    }

      upd_cls = &update->classes[cls_idx--];

      if (num_assign_reev_classes > 0)
    {
      a =
        pt_mvcc_set_spec_assign_reev_extra_indexes (parser, p, from, statement->info.update.assignment,
                            mvcc_assign_extra_classes, num_assign_reev_classes);
      if (a > 0)
        {
          upd_cls->mvcc_extra_assign_reev = regu_int_array_alloc (a);
          if (upd_cls->mvcc_extra_assign_reev == NULL)
        {
          error = er_errid ();
          goto cleanup;
        }
          memcpy (upd_cls->mvcc_extra_assign_reev, mvcc_assign_extra_classes, a * sizeof (int));
          upd_cls->num_extra_assign_reev = a;
        }
      else
        {
          upd_cls->mvcc_extra_assign_reev = NULL;
          upd_cls->num_extra_assign_reev = 0;
        }
    }
      /* count subclasses of current class */
      num_subclasses = 0;
      cl_name_node = p->info.spec.flat_entity_list;
      while (cl_name_node)
    {
      num_subclasses++;
      cl_name_node = cl_name_node->next;
    }
      upd_cls->num_subclasses = num_subclasses;

      /* count class assignments */
      a = 0;
      pt_init_assignments_helper (parser, &assign_helper, assigns);
      while (pt_get_next_assignment (&assign_helper) != NULL)
    {
      if (assign_helper.lhs->info.name.spec_id == p->info.spec.id)
        {
          a++;
        }
    }
      upd_cls->num_attrs = a;

      /* allocate array for subclasses OIDs, hfids, attributes ids, partitions */
      upd_cls->class_oid = regu_oid_array_alloc (num_subclasses);
      if (upd_cls->class_oid == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto cleanup;
    }

      regu_array_alloc (&upd_cls->class_hfid, num_subclasses);
      if (upd_cls->class_hfid == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto cleanup;
    }

      upd_cls->att_id = regu_int_array_alloc (num_subclasses * upd_cls->num_attrs);
      if (upd_cls->att_id == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto cleanup;
    }

      cl_name_node = p->info.spec.flat_entity_list;
      class_obj = cl_name_node->info.name.db_object;
      error = sm_partitioned_class_type (class_obj, &upd_cls->needs_pruning, NULL, NULL);
      if (error != NO_ERROR)
    {
      goto cleanup;
    }

      upd_cls->has_uniques = (p->info.spec.flag & PT_SPEC_FLAG_HAS_UNIQUE);

      /* iterate through subclasses */
      cl = 0;
      cl_name_node = p->info.spec.flat_entity_list;
      while (cl_name_node && error == NO_ERROR)
    {
      class_obj = cl_name_node->info.name.db_object;

      /* get class oid */
      class_oid = ws_identifier (class_obj);
      if (class_oid == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_HEAP_UNKNOWN_OBJECT, 3, 0, 0, 0);
          error = ER_HEAP_UNKNOWN_OBJECT;
          goto cleanup;
        }

      /* get hfid */
      hfid = sm_get_ch_heap (class_obj);
      if (hfid == NULL)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
          goto cleanup;
        }

      upd_cls->class_oid[cl] = *class_oid;
      upd_cls->class_hfid[cl] = *hfid;

      /* Calculate attribute ids and link each assignment to classes and attributes */
      pt_init_assignments_helper (parser, &assign_helper, assigns);
      assign_idx = a = 0;
      while ((att_name_node = pt_get_next_assignment (&assign_helper)) != NULL)
        {
          if (att_name_node->info.name.spec_id == cl_name_node->info.name.spec_id)
        {
          assign = &update->assigns[assign_idx];
          assign->cls_idx = cls_idx + 1;
          assign->att_idx = a;
          upd_cls->att_id[cl * upd_cls->num_attrs + a] =
            sm_att_id (class_obj, att_name_node->info.name.original);

          if (upd_cls->att_id[cl * upd_cls->num_attrs + a] < 0)
            {
              assert (er_errid () != NO_ERROR);
              error = er_errid ();
              goto cleanup;
            }
          /* count attributes for current class */
          a++;
        }
          /* count assignments */
          assign_idx++;
        }

      /* count subclasses */
      cl++;
      cl_name_node = cl_name_node->next;
    }
    }

  update->wait_msecs = XASL_WAIT_MSECS_NOCHANGE;
  hint_arg = statement->info.update.waitsecs_hint;
  if (statement->info.update.hint & PT_HINT_LK_TIMEOUT && PT_IS_HINT_NODE (hint_arg))
    {
      hint_wait_secs = (float) atof (hint_arg->info.name.original);
      if (hint_wait_secs > 0)
    {
      update->wait_msecs = (int) (hint_wait_secs * 1000);
    }
      else
    {
      update->wait_msecs = (int) hint_wait_secs;
    }
    }
  update->no_logging = (statement->info.update.hint & PT_HINT_NO_LOGGING);
  update->no_supplemental_log = (statement->info.update.hint & PT_HINT_NO_SUPPLEMENTAL_LOG);

  /* iterate through classes and check constants */
  for (p = from, cls_idx = num_classes; p; p = p->next)
    {
      /* ignore not updatable classes */
      if (!(p->info.spec.flag & PT_SPEC_FLAG_UPDATE))
    {
      continue;
    }
      upd_cls = &update->classes[--cls_idx];

      class_obj = p->info.spec.flat_entity_list->info.name.db_object;

      pt_init_assignments_helper (parser, &assign_helper, assigns);
      a = 0;
      while ((att_name_node = pt_get_next_assignment (&assign_helper)) != NULL)
    {
      PT_NODE *node, *prev, *next;
      /* process only constants assigned to current class attributes */
      if (att_name_node->info.name.spec_id != p->info.spec.id || !assign_helper.is_rhs_const)
        {
          /* this is a constant assignment */
          a++;
          continue;
        }
      /* get DB_VALUE of assignment's right argument */
      val = pt_value_to_db (parser, assign_helper.assignment->info.expr.arg2);
      if (val == NULL)
        {
          error = ER_GENERIC_ERROR;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
          goto cleanup;
        }

      prev = NULL;
      for (node = *non_null_attrs; node != NULL; node = next)
        {
          /* Check to see if this is a NON NULL attr */
          next = node->next;

          if (!pt_name_equal (parser, node, att_name_node))
        {
          prev = node;
          continue;
        }

          if (DB_IS_NULL (val))
        {
          /* assignment of a NULL value to a non null attribute */
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_ATTRIBUTE_CANT_BE_NULL, 1,
              att_name_node->info.name.original);
          error = ER_OBJ_ATTRIBUTE_CANT_BE_NULL;
          goto cleanup;
        }
          /* remove the node from the non_null_attrs list since we've already checked that the attr will be
           * non-null and the engine need not check again. */
          if (prev == NULL)
        {
          *non_null_attrs = (*non_null_attrs)->next;
        }
          else
        {
          prev->next = node->next;
        }

          /* free the node */
          node->next = NULL;    /* cut-off link */
          parser_free_tree (parser, node);
          break;
        }

      /* Coerce constant value to destination attribute type */
      regu_alloc (update->assigns[a].constant);
      if (update->assigns[a].constant == NULL)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
          goto cleanup;
        }
      attr = db_get_attribute (class_obj, att_name_node->info.name.original);
      if (attr == NULL)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
          goto cleanup;
        }
      dom = db_attribute_domain (attr);
      if (dom == NULL)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
          goto cleanup;
        }
      dom_status = tp_value_auto_cast (val, update->assigns[a].constant, dom);
      if (dom_status != DOMAIN_COMPATIBLE)
        {
          error = tp_domain_status_er_set (dom_status, ARG_FILE_LINE, val, dom);
          goto cleanup;
        }

      /* count assignments */
      a++;
    }
    }

  /* store number of ORDER BY keys in XASL tree */
  update->num_orderby_keys = (pt_length_of_list (aptr_statement->info.query.q.select.list)
                  - pt_length_of_select_list (aptr_statement->info.query.q.select.list,
                              EXCLUDE_HIDDEN_COLUMNS));
  assert (update->num_orderby_keys >= 0);

  /* generate xasl for non-null constraints predicates */
  error = pt_get_assignment_lists (parser, &select_names, &select_values, &const_names, &const_values, &num_vals,
                   &num_consts, statement->info.update.assignment, &links);
  if (error != NO_ERROR)
    {
      goto cleanup;
    }
  /* need to jump upd_del_class_cnt OID-CLASS OID pairs */
  error = pt_to_constraint_pred (parser, xasl, statement->info.update.spec, *non_null_attrs, select_names,
                 (aptr_statement->info.query.upd_del_class_cnt
                  + aptr_statement->info.query.mvcc_reev_extra_cls_cnt) * 2);
  pt_restore_assignment_links (statement->info.update.assignment, links, -1);
  if (error != NO_ERROR)
    {
      goto cleanup;
    }

  update->num_assign_reev_classes = 0;

  /* prepare data for MVCC condition reevaluation. For each class used in reevaluation (condition and assignement) set
   * the position (index) into select list. */

  for (cl_name_node = aptr_statement->info.query.q.select.list, cls_idx = 0, cl = 0;
       (cl_name_node != NULL
    && (cls_idx < (aptr_statement->info.query.upd_del_class_cnt
               + aptr_statement->info.query.mvcc_reev_extra_cls_cnt)));
       cl_name_node = cl_name_node->next->next, cls_idx++)
    {
      int idx;

      /* Find spec associated with current OID - CLASS OID pair */
      for (p = aptr_statement->info.query.q.select.from, idx = 0; p != NULL; p = p->next, idx++)
    {
      if (p->info.spec.id == cl_name_node->info.name.spec_id)
        {
          break;
        }
    }

      assert (p != NULL);

      if (PT_IS_SPEC_FLAG_SET (p, (PT_SPEC_FLAG_MVCC_COND_REEV | PT_SPEC_FLAG_MVCC_ASSIGN_REEV)))
    {
      /* Change index in FROM list with index in SELECT list for classes that appear in right side of assignements
       * but not in condition */
      if ((p->info.spec.flag & (PT_SPEC_FLAG_MVCC_COND_REEV | PT_SPEC_FLAG_MVCC_ASSIGN_REEV))
          == PT_SPEC_FLAG_MVCC_ASSIGN_REEV)
        {
          int idx1, idx2;

          for (idx1 = 0; idx1 < num_classes; idx1++)
        {
          upd_cls = &update->classes[idx1];
          for (idx2 = 0; idx2 < upd_cls->num_extra_assign_reev; idx2++)
            {
              if (upd_cls->mvcc_extra_assign_reev[idx2] == idx)
            {
              upd_cls->mvcc_extra_assign_reev[idx2] = cls_idx;
            }
            }
        }
          update->num_assign_reev_classes++;
        }

      /* set the position in SELECT list */
      update->mvcc_reev_classes[cl++] = cls_idx;
    }
    }

  /* fill in XASL cache related information */
  /* OID of the user who is creating this XASL */
  if ((oid = ws_identifier (db_get_user ())) != NULL)
    {
      COPY_OID (&xasl->creator_oid, oid);
    }
  else
    {
      OID_SET_NULL (&xasl->creator_oid);
    }


  /* list of class OIDs used in this XASL */
  assert (xasl->aptr_list != NULL);
  assert (xasl->class_oid_list == NULL);
  assert (xasl->class_locks == NULL);
  assert (xasl->tcard_list == NULL);

  if (xasl->aptr_list != NULL)
    {
      xasl->n_oid_list = xasl->aptr_list->n_oid_list;
      xasl->aptr_list->n_oid_list = 0;

      xasl->class_oid_list = xasl->aptr_list->class_oid_list;
      xasl->aptr_list->class_oid_list = NULL;

      xasl->class_locks = xasl->aptr_list->class_locks;
      xasl->aptr_list->class_locks = NULL;

      xasl->tcard_list = xasl->aptr_list->tcard_list;
      xasl->aptr_list->tcard_list = NULL;

      xasl->dbval_cnt = xasl->aptr_list->dbval_cnt;

      XASL_SET_FLAG (xasl, xasl->aptr_list->flag & XASL_INCLUDES_TDE_CLASS);
      XASL_CLEAR_FLAG (xasl->aptr_list, XASL_INCLUDES_TDE_CLASS);
    }

  xasl->query_alias = statement->alias_print;

  if (statement->info.update.limit)
    {
      PT_NODE *limit = statement->info.update.limit;

      if (limit->next)
    {
      xasl->limit_offset = pt_to_regu_variable (parser, limit, UNBOX_AS_VALUE);
      limit = limit->next;
    }
      xasl->limit_row_count = pt_to_regu_variable (parser, limit, UNBOX_AS_VALUE);
    }

cleanup:
  if (aptr_statement != NULL)
    {
      /* with clause should not be freed for later use in the subquery cache. */
      aptr_statement->info.query.with = NULL;
      parser_free_tree (parser, aptr_statement);
    }

  if (pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_SEMANTIC);
      xasl = NULL;
    }
  else if (error != NO_ERROR)
    {
      xasl = NULL;
    }
  return xasl;
}

/*
 * pt_find_omitted_default_expr() - Builds a list of attributes that have a default expression and are not found
 *                                  in the specified attributes list
 *   return: Error code
 *   parser(in/out): Parser context
 *   class_obj(in):
 *   specified_attrs(in): the list of attributes that are not to be considered
 *   default_expr_attrs(out):
 */
int
pt_find_omitted_default_expr (PARSER_CONTEXT * parser, DB_OBJECT * class_obj, PT_NODE * specified_attrs,
                  PT_NODE ** default_expr_attrs)
{
  SM_CLASS *cls;
  SM_ATTRIBUTE *att;
  int error = NO_ERROR;
  PT_NODE *new_attr = NULL, *node = NULL;

  if (default_expr_attrs == NULL)
    {
      assert (default_expr_attrs != NULL);
      return ER_FAILED;
    }

  error = au_fetch_class_force (class_obj, &cls, AU_FETCH_READ);
  if (error != NO_ERROR)
    {
      return error;
    }

  for (att = cls->attributes; att != NULL; att = (SM_ATTRIBUTE *) att->header.next)
    {
      /* skip if attribute has auto_increment */
      if (att->auto_increment != NULL)
    {
      continue;
    }

      /* skip if a value has already been specified for this attribute */
      for (node = specified_attrs; node != NULL; node = node->next)
    {
      if (!pt_str_compare (pt_get_name (node), att->header.name, CASE_INSENSITIVE))
        {
          break;
        }
    }
      if (node != NULL)
    {
      continue;
    }

      /* add attribute to default_expr_attrs list */
      new_attr = parser_new_node (parser, PT_NAME);
      if (new_attr == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return ER_FAILED;
    }

      new_attr->info.name.original = att->header.name;

      if (*default_expr_attrs != NULL)
    {
      new_attr->next = *default_expr_attrs;
      *default_expr_attrs = new_attr;
    }
      else
    {
      *default_expr_attrs = new_attr;
    }
    }

  return NO_ERROR;
}

/*
 * pt_append_omitted_on_update_expr_assignments() - Appends assignment expressions that have a default on update
 *                                                  expression and are not found in the specified attributes list
 *   return: Error code
 *   parser(in/out): Parser context
 *   assigns(in/out): assignment expr list
 *   from(in):
 */
int
pt_append_omitted_on_update_expr_assignments (PARSER_CONTEXT * parser, PT_NODE * assigns, PT_NODE * from)
{
  int error = NO_ERROR;

  for (PT_NODE * p = from; p != NULL; p = p->next)
    {
      if ((p->info.spec.flag & PT_SPEC_FLAG_UPDATE) == 0)
    {
      continue;
    }

      UINTPTR spec_id = p->info.spec.id;
      PT_NODE *cl_name_node = p->info.spec.flat_entity_list;
      DB_OBJECT *class_obj = cl_name_node->info.name.db_object;
      SM_CLASS *cls;
      SM_ATTRIBUTE *att;
      PT_NODE *new_lhs_of_assign = NULL;
      PT_NODE *default_expr_attrs = NULL;
      PT_ASSIGNMENTS_HELPER assign_helper;

      error = au_fetch_class_force (class_obj, &cls, AU_FETCH_READ);
      if (error != NO_ERROR)
    {
      return error;
    }

      for (att = cls->attributes; att != NULL; att = (SM_ATTRIBUTE *) att->header.next)
    {
      if (att->on_update_default_expr == DB_DEFAULT_NONE)
        {
          continue;
        }

      pt_init_assignments_helper (parser, &assign_helper, assigns);

      /* skip if already in the assign-list */
      PT_NODE *att_name_node = NULL;

      while ((att_name_node = pt_get_next_assignment (&assign_helper)) != NULL)
        {
          if (!pt_str_compare (att_name_node->info.name.original, att->header.name, CASE_INSENSITIVE)
          && att_name_node->info.name.spec_id == spec_id)
        {
          break;
        }
        }
      if (att_name_node != NULL)
        {
          continue;
        }

      /* add attribute to default_expr_attrs list */
      new_lhs_of_assign = parser_new_node (parser, PT_NAME);
      if (new_lhs_of_assign == NULL)
        {
          if (default_expr_attrs != NULL)
        {
          parser_free_tree (parser, default_expr_attrs);
        }
          PT_INTERNAL_ERROR (parser, "allocate new node");
          return ER_FAILED;
        }
      new_lhs_of_assign->info.name.original = att->header.name;
      new_lhs_of_assign->info.name.resolved = cls->header.ch_name;
      new_lhs_of_assign->info.name.spec_id = spec_id;

      PT_OP_TYPE op = pt_op_type_from_default_expr_type (att->on_update_default_expr);
      PT_NODE *new_rhs_of_assign = parser_make_expression (parser, op, NULL, NULL, NULL);
      if (new_rhs_of_assign == NULL)
        {
          if (new_lhs_of_assign != NULL)
        {
          parser_free_node (parser, new_lhs_of_assign);
        }
          if (default_expr_attrs != NULL)
        {
          parser_free_tree (parser, default_expr_attrs);
        }
          PT_INTERNAL_ERROR (parser, "allocate new node");
          return ER_FAILED;
        }

      PT_NODE *assign_expr = parser_make_expression (parser, PT_ASSIGN, new_lhs_of_assign, new_rhs_of_assign, NULL);
      if (assign_expr == NULL)
        {
          if (new_lhs_of_assign != NULL)
        {
          parser_free_node (parser, new_lhs_of_assign);
        }
          if (new_rhs_of_assign != NULL)
        {
          parser_free_node (parser, new_rhs_of_assign);
        }
          if (default_expr_attrs != NULL)
        {
          parser_free_tree (parser, default_expr_attrs);
        }
          PT_INTERNAL_ERROR (parser, "allocate new node");
          return ER_FAILED;
        }

      if (default_expr_attrs != NULL)
        {
          assign_expr->next = default_expr_attrs;
          default_expr_attrs = assign_expr;
        }
      else
        {
          default_expr_attrs = assign_expr;
        }
    }

      if (default_expr_attrs != NULL)
    {
      parser_append_node (default_expr_attrs, assigns);
    }
    }

  return NO_ERROR;
}

/*
 * parser_generate_xasl_pre () - builds xasl for query nodes,
 *                     and remembers uncorrelated queries
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
parser_generate_xasl_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  *continue_walk = PT_CONTINUE_WALK;

  if (parser->flag.abort)
    {
      *continue_walk = PT_STOP_WALK;
      return (node);
    }

  switch (node->node_type)
    {
    case PT_SELECT:
#if defined(CUBRID_DEBUG)
      PT_NODE_PRINT_TO_ALIAS (parser, node, PT_CONVERT_RANGE);
#endif /* CUBRID_DEBUG */

      [[fallthrough]];
    case PT_UNION:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
      /* The parser tree can be reused when multiple queries are executed through ux_execute_array (). */
      /* The XASL object has already been freed at pt_exit_packing_buf (), so only node->info.query.xasl is changed to null. */
      node->info.query.xasl = NULL;

      (void) pt_query_set_reference (parser, node);
      pt_push_symbol_info (parser, node);
      break;

    default:
      break;
    }

  if (pt_has_error (parser) || er_errid () == ER_LK_UNILATERALLY_ABORTED)
    {
      *continue_walk = PT_STOP_WALK;
    }

  return node;
}


/*
 * parser_generate_xasl_post () - builds xasl for query nodes
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in/out):
 */
static PT_NODE *
parser_generate_xasl_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  XASL_NODE *xasl;
  XASL_SUPP_INFO *info = (XASL_SUPP_INFO *) arg;

  if (*continue_walk == PT_STOP_WALK)
    {
      return node;
    }

  *continue_walk = PT_CONTINUE_WALK;

  if (parser->flag.abort)
    {
      *continue_walk = PT_STOP_WALK;
      return node;
    }

  assert (node != NULL);

  switch (node->node_type)
    {
    case PT_EXPR:
      if (PT_IS_SERIAL (node->info.expr.op))
    {
      /* fill in XASL cache related information; serial OID used in this XASL */
      if (pt_serial_to_xasl_class_oid_list (parser, node, &info->class_oid_list, &info->class_locks,
                        &info->tcard_list, &info->n_oid_list, &info->oid_list_size) < 0)
        {
          if (er_errid () == ER_OUT_OF_VIRTUAL_MEMORY)
        {
          PT_INTERNAL_ERROR (parser, "generate xasl");
        }
          xasl = NULL;
        }
    }
      break;

    case PT_SELECT:
    case PT_UNION:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
      assert (node->info.query.xasl == NULL);

      /* build XASL for the query */
      xasl = parser_generate_xasl_proc (parser, node, info->query_list);
      pt_pop_symbol_info (parser);
      if (node->node_type == PT_SELECT)
    {
      /* fill in XASL cache related information; list of class OIDs used in this XASL */
      if (xasl
          && pt_spec_to_xasl_class_oid_list (parser, node->info.query.q.select.from, &info->class_oid_list,
                         &info->class_locks, &info->tcard_list, &info->n_oid_list,
                         &info->oid_list_size, &info->includes_tde_class) < 0)
        {
          /* might be memory allocation error */
          PT_INTERNAL_ERROR (parser, "generate xasl");
          xasl = NULL;
        }
    }
      break;

    case PT_CTE:
      assert (node->info.cte.xasl == NULL || (parser->host_var_count == 0 && parser->auto_param_count == 0));
      xasl = parser_generate_xasl_proc (parser, node, info->query_list);
      break;

    default:
      break;
    }

  if (pt_has_error (parser) || er_errid () == ER_LK_UNILATERALLY_ABORTED)
    {
      *continue_walk = PT_STOP_WALK;
    }

  return node;
}


/*
 * parser_generate_xasl () - Creates xasl proc for parse tree.
 *   return:
 *   parser(in):
 *   node(in): pointer to a query structure
 */
XASL_NODE *
parser_generate_xasl (PARSER_CONTEXT * parser, PT_NODE * node)
{
  XASL_NODE *xasl = NULL;
  PT_NODE *next;
  bool is_system_generated_stmt;

  assert (parser != NULL && node != NULL);

  next = node->next;
  node->next = NULL;
  parser->dbval_cnt = 0;

  is_system_generated_stmt = node->flag.is_system_generated_stmt;

  node = parser_walk_tree (parser, node, pt_flush_class_and_null_xasl, NULL, pt_set_is_system_generated_stmt,
               &is_system_generated_stmt);

  /* During the above parser_walk_tree the request to get a driver may cause a deadlock. We give up the following steps
   * and propagate the error messages */
  if (parser->flag.abort || node == NULL)
    {
      return NULL;
    }

  switch (node->node_type)
    {
    case PT_SELECT:
    case PT_UNION:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
      /* do not treat the top level like a subquery, even if it is a subquery with respect to something else (eg
       * insert). */
      node->info.query.is_subquery = (PT_MISC_TYPE) 0;

      /* translate methods in queries to our internal form */
      if (node)
    {
      node = meth_translate (parser, node);
    }

      if (node)
    {
      /* This function might be called recursively by some queries. Therefore, if xasl_Supp_info has the allocated
       * memory blocks, we should release them to prevent memory leak. The following query is one of them.
       * scenario/medium/_02_xtests/xmother.sql delete from x where xstr > concat_str('string 4', 'string 40') on
       * (select y from y where yint = add_int(y, 10, 10)); NOTE: Defining xasl_Supp_info in local scope is one of
       * the alternative methods for preventing memory leak. However, it returns a wrong result of a query. */
      if (xasl_Supp_info.query_list)
        {
          parser_free_tree (parser, xasl_Supp_info.query_list);
        }
      /* add dummy node at the head of list */
      xasl_Supp_info.query_list = parser_new_node (parser, PT_SELECT);
      xasl_Supp_info.query_list->info.query.xasl = NULL;

      /* XASL cache related information */
      pt_init_xasl_supp_info ();

      db_query_plan_dump_fp_open ();

      node =
        parser_walk_tree (parser, node, parser_generate_xasl_pre, NULL, parser_generate_xasl_post, &xasl_Supp_info);

      /* close file handle for query plan if this function open it */
      db_query_plan_dump_fp_close ();

      parser_free_tree (parser, xasl_Supp_info.query_list);
      xasl_Supp_info.query_list = NULL;
    }

      if (node && !pt_has_error (parser))
    {
      node->next = next;
      xasl = (XASL_NODE *) node->info.query.xasl;
    }
      break;

    default:
      break;
    }

  scan_check_parallel_heap_scan_possible (xasl);
  check_parallel_subquery_possible (xasl);

  /* fill in XASL cache related information */
  if (xasl)
    {
      OID *oid = NULL;
      int n;
      DB_OBJECT *user = NULL;

      /* OID of the user who is creating this XASL */
      user = db_get_user ();
      if (user != NULL)
    {
      oid = ws_identifier (user);
    }

      if (user != NULL && oid != NULL)
    {
      COPY_OID (&xasl->creator_oid, oid);
    }
      else
    {
      OID_SET_NULL (&xasl->creator_oid);
    }

      /* list of class OIDs used in this XASL */
      xasl->n_oid_list = 0;
      xasl->class_oid_list = NULL;
      xasl->class_locks = NULL;
      xasl->tcard_list = NULL;
      XASL_CLEAR_FLAG (xasl, XASL_INCLUDES_TDE_CLASS);

      if ((n = xasl_Supp_info.n_oid_list) > 0 && (xasl->class_oid_list = regu_oid_array_alloc (n))
      && (xasl->class_locks = regu_int_array_alloc (n)) && (xasl->tcard_list = regu_int_array_alloc (n)))
    {
      xasl->n_oid_list = n;
      (void) memcpy (xasl->class_oid_list, xasl_Supp_info.class_oid_list, sizeof (OID) * n);
      (void) memcpy (xasl->class_locks, xasl_Supp_info.class_locks, sizeof (int) * n);
      (void) memcpy (xasl->tcard_list, xasl_Supp_info.tcard_list, sizeof (int) * n);
      if (xasl_Supp_info.includes_tde_class == 1)
        {
          XASL_SET_FLAG (xasl, XASL_INCLUDES_TDE_CLASS);
        }
    }

      xasl->dbval_cnt = parser->dbval_cnt;
    }

  /* free what were allocated in pt_spec_to_xasl_class_oid_list() */
  pt_init_xasl_supp_info ();

  if (xasl)
    {
      xasl->query_alias = node->alias_print;
      XASL_SET_FLAG (xasl, XASL_TOP_MOST_XASL);
    }

  if (prm_get_bool_value (PRM_ID_XASL_DEBUG_DUMP))
    {
      if (xasl)
    {
      if (xasl->query_alias == NULL)
        {
          if (node->alias_print == NULL)
        {
          node->alias_print = parser_print_tree (parser, node);
        }

          xasl->query_alias = node->alias_print;
        }

      qdump_print_xasl (xasl);
    }
      else
    {
      printf ("<NULL XASL generation>\n");
    }
    }

  return xasl;
}


/*
 * pt_set_level_node_etc_pre () -
 *   return:
 *   parser(in):
 *   node(in/out):
 *   arg(in/out):
 *   continue_walk(in):
 */
static PT_NODE *
pt_set_level_node_etc_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  DB_VALUE **level_valp = (DB_VALUE **) arg;

  if (node->node_type == PT_EXPR)
    {
      if (node->info.expr.op == PT_LEVEL)
    {
      if (*level_valp == NULL)
        {
          regu_alloc (*level_valp);
        }

      node->etc = *level_valp;
    }
    }

  return node;
}

/*
 * pt_set_level_node_etc () - set the db val ponter for LEVEL nodes
 *   return:
 *   parser(in):
 *   node_list(in):
 *   level_valp(out):
 */
void
pt_set_level_node_etc (PARSER_CONTEXT * parser, PT_NODE * node_list, DB_VALUE ** level_valp)
{
  PT_NODE *node, *save_node, *save_next;

  if (node_list)
    {
      for (node = node_list; node; node = node->next)
    {
      save_node = node;

      CAST_POINTER_TO_NODE (node);

      /* save and cut-off node link */
      save_next = node->next;
      node->next = NULL;

      (void) parser_walk_tree (parser, node, pt_set_level_node_etc_pre, level_valp, NULL, NULL);

      if (node)
        {
          node->next = save_next;
        }

      node = save_node;
    }           /* for (node = ...) */
    }
}

/*
 * pt_make_regu_level () - make a regu_variable constant for LEVEL
 *   return:
 *   parser(in):
 *   node(in):
 */
static REGU_VARIABLE *
pt_make_regu_level (PARSER_CONTEXT * parser, const PT_NODE * node)
{
  REGU_VARIABLE *regu = NULL;
  DB_VALUE *dbval;

  dbval = (DB_VALUE *) node->etc;

  if (dbval)
    {
      regu_alloc (regu);
      if (regu)
    {
      regu->type = TYPE_CONSTANT;
      regu->domain = &tp_Integer_domain;
      regu->value.dbvalptr = dbval;
    }
    }
  else
    {
      PT_INTERNAL_ERROR (parser, "generate LEVEL");
    }

  return regu;
}

/*
 * pt_set_isleaf_node_etc_pre () -
 *   return:
 *   parser(in):
 *   node(in/out):
 *   arg(in/out):
 *   continue_walk(in):
 */
static PT_NODE *
pt_set_isleaf_node_etc_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  DB_VALUE **isleaf_valp = (DB_VALUE **) arg;

  if (node->node_type == PT_EXPR)
    {
      if (node->info.expr.op == PT_CONNECT_BY_ISLEAF)
    {
      if (*isleaf_valp == NULL)
        {
          regu_alloc (*isleaf_valp);
        }

      node->etc = *isleaf_valp;
    }
    }

  return node;
}

/*
 * pt_set_isleaf_node_etc () - set the db val ponter for CONNECT_BY_ISLEAF nodes
 *   return:
 *   parser(in):
 *   node_list(in):
 *   isleaf_valp(out):
 */
void
pt_set_isleaf_node_etc (PARSER_CONTEXT * parser, PT_NODE * node_list, DB_VALUE ** isleaf_valp)
{
  PT_NODE *node, *save_node, *save_next;

  if (node_list)
    {
      for (node = node_list; node; node = node->next)
    {
      save_node = node;

      CAST_POINTER_TO_NODE (node);

      /* save and cut-off node link */
      save_next = node->next;
      node->next = NULL;

      (void) parser_walk_tree (parser, node, pt_set_isleaf_node_etc_pre, isleaf_valp, NULL, NULL);

      if (node)
        {
          node->next = save_next;
        }

      node = save_node;
    }           /* for (node = ...) */
    }
}

/*
 * pt_make_regu_isleaf () - make a regu_variable constant for CONNECT_BY_ISLEAF
 *   return:
 *   parser(in):
 *   node(in):
 */
static REGU_VARIABLE *
pt_make_regu_isleaf (PARSER_CONTEXT * parser, const PT_NODE * node)
{
  REGU_VARIABLE *regu = NULL;
  DB_VALUE *dbval;

  dbval = (DB_VALUE *) node->etc;

  if (dbval)
    {
      regu_alloc (regu);
      if (regu)
    {
      regu->type = TYPE_CONSTANT;
      regu->domain = &tp_Integer_domain;
      regu->value.dbvalptr = dbval;
    }
    }
  else
    {
      PT_INTERNAL_ERROR (parser, "generate CONNECT_BY_ISLEAF");
    }

  return regu;
}


/*
 * pt_set_iscycle_node_etc_pre () -
 *   return:
 *   parser(in):
 *   node(in/out):
 *   arg(in/out):
 *   continue_walk(in):
 */
static PT_NODE *
pt_set_iscycle_node_etc_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  DB_VALUE **iscycle_valp = (DB_VALUE **) arg;

  if (node->node_type == PT_EXPR)
    {
      if (node->info.expr.op == PT_CONNECT_BY_ISCYCLE)
    {
      if (*iscycle_valp == NULL)
        {
          regu_alloc (*iscycle_valp);
        }

      node->etc = *iscycle_valp;
    }
    }

  return node;
}

/*
 * pt_set_iscycle_node_etc () - set the db val ponter for CONNECT_BY_ISCYCLE nodes
 *   return:
 *   parser(in):
 *   node_list(in):
 *   iscycle_valp(out):
 */
void
pt_set_iscycle_node_etc (PARSER_CONTEXT * parser, PT_NODE * node_list, DB_VALUE ** iscycle_valp)
{
  PT_NODE *node, *save_node, *save_next;

  if (node_list)
    {
      for (node = node_list; node; node = node->next)
    {
      save_node = node;

      CAST_POINTER_TO_NODE (node);

      /* save and cut-off node link */
      save_next = node->next;
      node->next = NULL;

      (void) parser_walk_tree (parser, node, pt_set_iscycle_node_etc_pre, iscycle_valp, NULL, NULL);

      if (node)
        {
          node->next = save_next;
        }

      node = save_node;
    }           /* for (node = ...) */
    }
}

/*
 * pt_make_regu_iscycle () - make a regu_variable constant for CONNECT_BY_ISCYCLE
 *   return:
 *   parser(in):
 *   node(in):
 */
static REGU_VARIABLE *
pt_make_regu_iscycle (PARSER_CONTEXT * parser, const PT_NODE * node)
{
  REGU_VARIABLE *regu = NULL;
  DB_VALUE *dbval;

  dbval = (DB_VALUE *) node->etc;

  if (dbval)
    {
      regu_alloc (regu);
      if (regu)
    {
      regu->type = TYPE_CONSTANT;
      regu->domain = &tp_Integer_domain;
      regu->value.dbvalptr = dbval;
    }
    }
  else
    {
      PT_INTERNAL_ERROR (parser, "generate CONNECT_BY_ISCYCLE");
    }

  return regu;
}

/*
 * pt_set_connect_by_operator_node_etc_pre () -
 *   return:
 *   parser(in):
 *   node(in/out):
 *   arg(in/out):
 *   continue_walk(in):
 */
static PT_NODE *
pt_set_connect_by_operator_node_etc_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  XASL_NODE *xasl = (XASL_NODE *) arg;

  if (node->node_type == PT_EXPR)
    {
      if (node->info.expr.op == PT_CONNECT_BY_ROOT || node->info.expr.op == PT_SYS_CONNECT_BY_PATH)
    {
      node->etc = xasl;
    }
    }

  return node;
}

/*
 * pt_set_connect_by_operator_node_etc () - set the select xasl pointer into
 *    etc of PT_NODEs which are CONNECT BY operators/functions
 *   return:
 *   parser(in):
 *   node_list(in):
 *   xasl(in):
 */
void
pt_set_connect_by_operator_node_etc (PARSER_CONTEXT * parser, PT_NODE * node_list, XASL_NODE * xasl)
{
  PT_NODE *node, *save_node, *save_next;

  if (node_list)
    {
      for (node = node_list; node; node = node->next)
    {
      save_node = node;

      CAST_POINTER_TO_NODE (node);

      /* save and cut-off node link */
      save_next = node->next;
      node->next = NULL;

      (void) parser_walk_tree (parser, node, pt_set_connect_by_operator_node_etc_pre, (void *) xasl, NULL, NULL);

      if (node)
        {
          node->next = save_next;
        }

      node = save_node;
    }           /* for (node = ...) */
    }
}

/*
 * pt_set_qprior_node_etc_pre () -
 *   return:
 *   parser(in):
 *   node(in/out):
 *   arg(in/out):
 *   continue_walk(in):
 */
static PT_NODE *
pt_set_qprior_node_etc_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  XASL_NODE *xasl = (XASL_NODE *) arg;

  if (node->node_type == PT_EXPR)
    {
      if (node->info.expr.op == PT_PRIOR)
    {
      node->etc = xasl;
      node->info.expr.op = PT_QPRIOR;
    }
    }
  else if (node->node_type == PT_SELECT || node->node_type == PT_UNION || node->node_type == PT_DIFFERENCE
       || node->node_type == PT_INTERSECTION)
    {
      *continue_walk = PT_STOP_WALK;
    }

  return node;
}

/*
 * pt_set_qprior_node_etc () - set the select xasl pointer into
 *    etc of PRIOR operator in select list; modifies the operator
 *    to eliminate any confusion with PRIOR in CONNECT BY clause
 *   return:
 *   parser(in):
 *   node_list(in):
 *   xasl(in):
 */
void
pt_set_qprior_node_etc (PARSER_CONTEXT * parser, PT_NODE * node_list, XASL_NODE * xasl)
{
  PT_NODE *node, *save_node, *save_next;

  if (node_list)
    {
      for (node = node_list; node; node = node->next)
    {
      save_node = node;

      CAST_POINTER_TO_NODE (node);

      /* save and cut-off node link */
      save_next = node->next;
      node->next = NULL;

      (void) parser_walk_tree (parser, node, pt_set_qprior_node_etc_pre, (void *) xasl, NULL, NULL);

      if (node)
        {
          node->next = save_next;
        }

      node = save_node;
    }           /* for (node = ...) */
    }
}

/*
 * pt_make_outlist_from_vallist () - make an outlist with const regu
 *    variables from a vallist
 *   return:
 *   parser(in):
 *   val_list_p(in):
 */
static OUTPTR_LIST *
pt_make_outlist_from_vallist (PARSER_CONTEXT * parser, VAL_LIST * val_list_p)
{
  QPROC_DB_VALUE_LIST vallist = val_list_p->valp;
  REGU_VARIABLE_LIST regulist = NULL, regu_list = NULL;
  int i;

  OUTPTR_LIST *outptr_list = NULL;
  regu_alloc (outptr_list);
  if (!outptr_list)
    {
      return NULL;
    }

  outptr_list->valptr_cnt = val_list_p->val_cnt;
  outptr_list->valptrp = NULL;

  for (i = 0; i < val_list_p->val_cnt; i++)
    {
      regu_alloc (regu_list);

      if (!outptr_list->valptrp)
    {
      outptr_list->valptrp = regu_list;
      regulist = regu_list;
    }

      regu_list->next = NULL;
      regu_list->value.type = TYPE_CONSTANT;
      regu_list->value.domain = vallist->dom;
      regu_list->value.value.dbvalptr = vallist->val;

      if (regulist != regu_list)
    {
      regulist->next = regu_list;
      regulist = regu_list;
    }

      vallist = vallist->next;
    }

  return outptr_list;
}

/*
 * pt_make_pos_regu_list () - makes a list of positional regu variables
 *  for the given vallist
 *    return:
 *  parser(in):
 *  val_list_p(in):
 */
static REGU_VARIABLE_LIST
pt_make_pos_regu_list (PARSER_CONTEXT * parser, VAL_LIST * val_list_p)
{
  REGU_VARIABLE_LIST regu_list = NULL;
  REGU_VARIABLE_LIST *tail = NULL;
  REGU_VARIABLE *regu;
  QPROC_DB_VALUE_LIST valp;
  int i = 0;

  tail = &regu_list;

  for (valp = val_list_p->valp; valp != NULL; valp = valp->next)
    {
      regu_alloc (*tail);

      regu = pt_make_pos_regu_var_from_scratch (valp->dom, valp->val, i);
      i++;

      if (regu && *tail)
    {
      (*tail)->value = *regu;
      tail = &(*tail)->next;
    }
      else
    {
      regu_list = NULL;
      break;
    }
    }
  return regu_list;
}

void
pt_sort_pos_regu_list_by_pos_no (REGU_VARIABLE_LIST * list_ptr)
{
  REGU_VARIABLE_LIST current, next;
  REGU_VARIABLE temp;
  bool swapped;

  if (list_ptr == NULL || *list_ptr == NULL || (*list_ptr)->next == NULL)
    {
      return;
    }

  do
    {
      swapped = false;
      current = *list_ptr;

      while (current->next != NULL)
    {
      assert (current->value.type == TYPE_POSITION);
      next = current->next;

      if (current->value.value.pos_descr.pos_no > next->value.value.pos_descr.pos_no)
        {
          temp = current->value;
          current->value = next->value;
          next->value = temp;
          swapped = true;
        }
      current = current->next;
    }
    }
  while (swapped);
}

/*
 * pt_copy_val_list () - makes a copy of the given val list, allocating
 *  a new VAL_LIST and DB_VALUEs
 *    return:
 *  parser(in):
 *  val_list_p(in):
 */
static VAL_LIST *
pt_copy_val_list (PARSER_CONTEXT * parser, VAL_LIST * val_list_p)
{
  QPROC_DB_VALUE_LIST dblist1, dblist2;
  VAL_LIST *new_val_list;

  if (!val_list_p)
    {
      return NULL;
    }

  regu_alloc (new_val_list);
  if (!new_val_list)
    {
      return NULL;
    }

  dblist2 = NULL;
  new_val_list->val_cnt = 0;

  for (dblist1 = val_list_p->valp; dblist1; dblist1 = dblist1->next)
    {
      if (!dblist2)
    {
      regu_alloc (new_val_list->valp);  /* don't alloc DB_VALUE */
      dblist2 = new_val_list->valp;
    }
      else
    {
      regu_alloc (dblist2->next);
      dblist2 = dblist2->next;
    }

      dblist2->val = db_value_copy (dblist1->val);
      dblist2->dom = dblist1->dom;
      new_val_list->val_cnt++;
    }

  return new_val_list;
}

/*
 * pt_fix_pseudocolumns_pos_regu_list () - modifies pseudocolumns positional
 *  regu variables in list to fetch into node->etc
 *    return:
 *  parser(in):
 *  node_list(in):
 *  regu_list(in/out):
 */
static void
pt_fix_pseudocolumns_pos_regu_list (PARSER_CONTEXT * parser, PT_NODE * node_list, REGU_VARIABLE_LIST regu_list)
{
  PT_NODE *node, *saved;
  REGU_VARIABLE_LIST rl;

  for (node = node_list, rl = regu_list; node != NULL && rl != NULL; node = node->next, rl = rl->next)
    {
      saved = node;
      CAST_POINTER_TO_NODE (node);

      if (node->node_type == PT_EXPR
      && (node->info.expr.op == PT_LEVEL || node->info.expr.op == PT_CONNECT_BY_ISLEAF
          || node->info.expr.op == PT_CONNECT_BY_ISCYCLE))
    {
      rl->value.vfetch_to = (DB_VALUE *) node->etc;
    }

      node = saved;
    }
}

/*
 * pt_split_pred_regu_list () - splits regu list(s) into pred and rest
 *    return:
 *  parser(in):
 *  val_list(in):
 *  pred(in):
 *  regu_list_rest(in/out):
 *  regu_list_pred(out):
 *  prior_regu_list_rest(in/out):
 *  prior_regu_list_pred(out):
 *  split_prior(in):
 *  regu_list(in/out):
 */
static int
pt_split_pred_regu_list (PARSER_CONTEXT * parser, const VAL_LIST * val_list, const PRED_EXPR * pred,
             REGU_VARIABLE_LIST * regu_list_rest, REGU_VARIABLE_LIST * regu_list_pred,
             REGU_VARIABLE_LIST * prior_regu_list_rest, REGU_VARIABLE_LIST * prior_regu_list_pred,
             bool split_prior)
{
  QPROC_DB_VALUE_LIST valp = NULL;
  PRED_REGU_VARIABLE_P_LIST regu_p_list = NULL, list = NULL;
  REGU_VARIABLE_LIST rl = NULL, prev_rl = NULL;
  REGU_VARIABLE_LIST prior_rl = NULL, prev_prior_rl = NULL;
  REGU_VARIABLE_LIST rl_next = NULL, prior_rl_next = NULL;
  bool moved_rl = false, moved_prior_rl = false;
  int err = NO_ERROR;

  regu_p_list = pt_get_pred_regu_variable_p_list (pred, &err);
  if (err != NO_ERROR)
    {
      goto exit_on_error;
    }
  if (!regu_p_list)
    {
      /* predicate is not referencing any of the DB_VALUEs in val_list */
      return NO_ERROR;
    }

  rl = *regu_list_rest;
  prev_rl = NULL;

  if (split_prior)
    {
      prior_rl = *prior_regu_list_rest;
      prev_prior_rl = NULL;
    }

  for (valp = val_list->valp; valp != NULL; valp = valp->next)
    {
      moved_rl = false;
      moved_prior_rl = false;

      for (list = regu_p_list; list != NULL; list = list->next)
    {
      if (list->pvalue->value.dbvalptr == valp->val)
        {
          if (split_prior && list->is_prior)
        {
          if (!moved_prior_rl)
            {
              prior_rl_next = prior_rl->next;
              /* move from prior_regu_list_rest into prior_regu_list_pred */
              pt_add_regu_var_to_list (prior_regu_list_pred, prior_rl);
              if (!prev_prior_rl)
            {
              /* moved head of the list */
              prior_rl = *prior_regu_list_rest = prior_rl_next;
            }
              else
            {
              prev_prior_rl->next = prior_rl_next;
              prior_rl = prior_rl_next;
            }
              moved_prior_rl = true;
            }
        }
          else
        {
          if (!moved_rl)
            {
              rl_next = rl->next;
              /* move from regu_list_rest into regu_list_pred */
              pt_add_regu_var_to_list (regu_list_pred, rl);
              if (!prev_rl)
            {
              /* moved head of the list */
              rl = *regu_list_rest = rl_next;
            }
              else
            {
              prev_rl->next = rl_next;
              rl = rl_next;
            }
              moved_rl = true;
            }
        }

          if (moved_rl && moved_prior_rl)
        {
          break;
        }
        }
    }

      if (!moved_rl)
    {
      prev_rl = rl;
      rl = rl->next;
    }
      if (!moved_prior_rl && split_prior)
    {
      prev_prior_rl = prior_rl;
      prior_rl = prior_rl->next;
    }
    }

  while (regu_p_list)
    {
      list = regu_p_list->next;
      free (regu_p_list);
      regu_p_list = list;
    }

  return NO_ERROR;

exit_on_error:

  while (regu_p_list)
    {
      list = regu_p_list->next;
      free (regu_p_list);
      regu_p_list = list;
    }

  return ER_FAILED;
}

/*
 * pt_get_pred_regu_variable_p_list () - returns a list of pointers to
 *  constant regu variables in the predicate
 *    return:
 *  pred(in):
 *  err(out):
 */
static PRED_REGU_VARIABLE_P_LIST
pt_get_pred_regu_variable_p_list (const PRED_EXPR * pred, int *err)
{
  PRED_REGU_VARIABLE_P_LIST head = NULL, nextl = NULL, nextr = NULL, tail = NULL;

  if (!pred)
    {
      return NULL;
    }

  switch (pred->type)
    {
    case T_PRED:
      nextl = pt_get_pred_regu_variable_p_list (pred->pe.m_pred.lhs, err);
      nextr = pt_get_pred_regu_variable_p_list (pred->pe.m_pred.rhs, err);
      break;

    case T_EVAL_TERM:
      switch (pred->pe.m_eval_term.et_type)
    {
    case T_COMP_EVAL_TERM:
      nextl = pt_get_var_regu_variable_p_list (pred->pe.m_eval_term.et.et_comp.lhs, false, err);
      nextr = pt_get_var_regu_variable_p_list (pred->pe.m_eval_term.et.et_comp.rhs, false, err);
      break;

    case T_ALSM_EVAL_TERM:
      nextl = pt_get_var_regu_variable_p_list (pred->pe.m_eval_term.et.et_alsm.elem, false, err);
      nextr = pt_get_var_regu_variable_p_list (pred->pe.m_eval_term.et.et_alsm.elemset, false, err);
      break;

    case T_LIKE_EVAL_TERM:
      nextl = pt_get_var_regu_variable_p_list (pred->pe.m_eval_term.et.et_like.pattern, false, err);
      nextr = pt_get_var_regu_variable_p_list (pred->pe.m_eval_term.et.et_like.src, false, err);
      break;

    case T_RLIKE_EVAL_TERM:
      nextl = pt_get_var_regu_variable_p_list (pred->pe.m_eval_term.et.et_rlike.pattern, false, err);
      nextr = pt_get_var_regu_variable_p_list (pred->pe.m_eval_term.et.et_rlike.src, false, err);
      break;
    }
      break;

    case T_NOT_TERM:
      nextl = pt_get_pred_regu_variable_p_list (pred->pe.m_not_term, err);
      break;
    }

  if (nextl)
    {
      if (!head)
    {
      head = tail = nextl;
    }
      else
    {
      tail->next = nextl;
    }
      while (tail->next)
    {
      tail = tail->next;
    }
    }
  if (nextr)
    {
      if (!head)
    {
      head = tail = nextr;
    }
      else
    {
      tail->next = nextr;
    }
      while (tail->next)
    {
      tail = tail->next;
    }
    }

  return head;
}

/*
 * pt_get_var_regu_variable_p_list () - returns a list of pointers to
 *  constant regu variables referenced by the argument regu variable
 *  (or the argument regu variable itself)
 *    return:
 *  regu(in): the regu variable
 *  is_prior(in): is it in PRIOR argument expression?
 *  err(out):
 */
static PRED_REGU_VARIABLE_P_LIST
pt_get_var_regu_variable_p_list (const REGU_VARIABLE * regu, bool is_prior, int *err)
{
  PRED_REGU_VARIABLE_P_LIST list = NULL;
  PRED_REGU_VARIABLE_P_LIST list1 = NULL, list2 = NULL, list3 = NULL;

  if (regu == NULL)
    {
      return NULL;
    }

  switch (regu->type)
    {
    case TYPE_CONSTANT:
      list = (PRED_REGU_VARIABLE_P_LIST) malloc (sizeof (PRED_REGU_VARIABLE_P_LIST_NODE));
      if (list)
    {
      list->pvalue = regu;
      list->is_prior = is_prior;
      list->next = NULL;
    }
      else
    {
      *err = ER_FAILED;
    }
      break;

    case TYPE_INARITH:
    case TYPE_OUTARITH:
      if (regu->value.arithptr->opcode == T_PRIOR)
    {
      list = pt_get_var_regu_variable_p_list (regu->value.arithptr->rightptr, true, err);
    }
      else
    {
      list1 = pt_get_var_regu_variable_p_list (regu->value.arithptr->leftptr, is_prior, err);
      list2 = pt_get_var_regu_variable_p_list (regu->value.arithptr->rightptr, is_prior, err);
      list3 = pt_get_var_regu_variable_p_list (regu->value.arithptr->thirdptr, is_prior, err);
      list = list1;
      if (!list)
        {
          list = list2;
        }
      else
        {
          while (list1->next)
        {
          list1 = list1->next;
        }
          list1->next = list2;
        }
      if (!list)
        {
          list = list3;
        }
      else
        {
          list1 = list;
          while (list1->next)
        {
          list1 = list1->next;
        }
          list1->next = list3;
        }
    }
      break;

    case TYPE_FUNC:
      {
    REGU_VARIABLE_LIST r = regu->value.funcp->operand;
    while (r)
      {
        list1 = pt_get_var_regu_variable_p_list (&r->value, is_prior, err);

        if (!list)
          {
        list = list1;
          }
        else
          {
        list2 = list;
        while (list2->next)
          {
            list2 = list2->next;
          }
        list2->next = list1;
          }

        r = r->next;
      }
      }
      break;

    case TYPE_SP:
      {
    REGU_VARIABLE_LIST r = regu->value.sp_ptr->args;
    while (r)
      {
        list1 = pt_get_var_regu_variable_p_list (&r->value, is_prior, err);

        if (!list)
          {
        list = list1;
          }
        else
          {
        list2 = list;
        while (list2->next)
          {
            list2 = list2->next;
          }
        list2->next = list1;
          }

        r = r->next;
      }
      }
      break;

    default:
      break;
    }

  return list;
}

/*
 * pt_add_regu_var_to_list () - adds a regu list node to another regu list
 *  return:
 *  destination (in/out)  :
 *  source (in/out)       :
 */
static void
pt_add_regu_var_to_list (REGU_VARIABLE_LIST * destination, REGU_VARIABLE_LIST source)
{
  source->next = NULL;

  pt_merge_regu_var_lists (destination, source);
}

/*
 * pt_merge_regu_var_lists () - appends the source to the end of the destination regu var list
 *  return:
 *  destination (in/out):
 *  source (in/out):
 */
static void
pt_merge_regu_var_lists (REGU_VARIABLE_LIST * destination, REGU_VARIABLE_LIST source)
{
  REGU_VARIABLE_LIST itr;

  if ((*destination) == NULL)
    {
      *destination = source;
    }
  else
    {
      // get the end of the list
      for (itr = *destination; itr->next != NULL; itr = itr->next)
    ;

      // append it
      itr->next = source;
    }
}

/*
 * pt_build_do_stmt_aptr_list_pre () - build an XASL list of top level queries
 * returns: original node
 *  node(in): node to check
 *  arg(out): first node in list
 */
static PT_NODE *
pt_build_do_stmt_aptr_list_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  if (arg == NULL)
    {
      /* function was called with wrong params */
      assert (false);
      return NULL;
    }

  if (node == NULL)
    {
      /* nothing to do */
      return NULL;
    }

  if (PT_IS_QUERY_NODE_TYPE (node->node_type) && node->info.query.correlation_level == 0)
    {
      XASL_NODE **out_xasl = (XASL_NODE **) arg;
      XASL_NODE *aptr_list = *((XASL_NODE **) arg);
      XASL_NODE *xasl = NULL;

      *continue_walk = PT_LIST_WALK;

      /* generate query XASL */
      xasl = parser_generate_xasl (parser, node);
      if (xasl == NULL)
    {
      /* error generating xasl; check for parser messages */
      if (pt_has_error (parser))
        {
          pt_report_to_ersys_with_statement (parser, PT_EXECUTION, node);
        }

      return node;
    }

      if (aptr_list != NULL)
    {
      /* list is not empty, append our XASL node */
      while (aptr_list->next)
        {
          aptr_list = aptr_list->next;
        }

      aptr_list->next = xasl;
    }
      else
    {
      /* first found query node */
      *out_xasl = xasl;
    }
    }

  return node;
}

/*
 * pt_build_do_stmt_aptr_list () - search for top level queries in node and
 *                                 build an XASL list
 * returns: XASL node list
 *  node(in): parser node to search in
 *
 * NOTE: search includes specified node (if node is a query, it will be
 *       returned).
 */
static XASL_NODE *
pt_build_do_stmt_aptr_list (PARSER_CONTEXT * parser, PT_NODE * node)
{
  XASL_NODE *out_node = NULL;

  parser_walk_tree (parser, node, pt_build_do_stmt_aptr_list_pre, &out_node, pt_continue_walk, NULL);

  return out_node;
}

/*
 * parser_generate_do_stmt_xasl () - Generate xasl for DO statement
 *   return:
 *   parser(in):
 *   node(in):
 */
XASL_NODE *
parser_generate_do_stmt_xasl (PARSER_CONTEXT * parser, PT_NODE * node)
{
  XASL_NODE *xasl = NULL;
  OID *oid;
  DB_OBJECT *user = NULL;

  /* check parameters */
  assert (parser != NULL && node != NULL);

  if (node->node_type != PT_DO)
    {
      return NULL;
    }

  if (node->info.do_.expr == NULL)
    {
      /* do not accept NULL expressions */
      assert (false);
      return NULL;
    }

  parser->dbval_cnt = 0;

  xasl = regu_xasl_node_alloc (DO_PROC);
  if (!xasl)
    {
      return NULL;
    }

  /* populate statement's aptr_list; in this context, uncorrelated subqueries mean top level queries in expr tree */
  xasl->aptr_list = pt_build_do_stmt_aptr_list (parser, node);
  if (er_errid () != NO_ERROR)
    {
      return NULL;
    }

  if (xasl->aptr_list != NULL)
    {
      XASL_SET_FLAG (xasl, xasl->aptr_list->flag & XASL_INCLUDES_TDE_CLASS);
    }

  xasl->outptr_list = pt_to_outlist (parser, node->info.do_.expr, NULL, UNBOX_AS_VALUE);
  if (!xasl->outptr_list)
    {
      return NULL;
    }

  /* OID of the user who is creating this XASL */
  if ((user = db_get_user ()) != NULL && (oid = ws_identifier (user)) != NULL)
    {
      COPY_OID (&xasl->creator_oid, oid);
    }
  else
    {
      OID_SET_NULL (&xasl->creator_oid);
    }

  xasl->n_oid_list = 0;
  xasl->class_oid_list = NULL;
  xasl->class_locks = NULL;
  xasl->tcard_list = NULL;
  xasl->dbval_cnt = parser->dbval_cnt;
  xasl->query_alias = node->alias_print;
  XASL_SET_FLAG (xasl, XASL_TOP_MOST_XASL);

  if (prm_get_bool_value (PRM_ID_XASL_DEBUG_DUMP))
    {
      if (xasl->query_alias == NULL)
    {
      if (node->alias_print == NULL)
        {
          node->alias_print = parser_print_tree (parser, node);
        }
      xasl->query_alias = node->alias_print;
    }
      qdump_print_xasl (xasl);
    }

  return xasl;
}

/*
 * pt_to_order_siblings_by () - modify order by list to match tuples used
 *                              at order siblings by execution
 *   return:
 *   parser(in):
 *   node(in):
 */
static SORT_LIST *
pt_to_order_siblings_by (PARSER_CONTEXT * parser, XASL_NODE * xasl, XASL_NODE * connect_by_xasl)
{
  SORT_LIST *orderby;
  REGU_VARIABLE_LIST regu_list1, regu_list2;
  int i, j;

  if (!xasl || !xasl->outptr_list || !connect_by_xasl || !connect_by_xasl->outptr_list)
    {
      return NULL;
    }

  for (orderby = xasl->orderby_list; orderby; orderby = orderby->next)
    {
      for (i = 0, regu_list1 = xasl->outptr_list->valptrp; regu_list1; regu_list1 = regu_list1->next, i++)
    {
      if (i == orderby->pos_descr.pos_no)
        {
          if (regu_list1->value.type != TYPE_CONSTANT)
        {
          PT_INTERNAL_ERROR (parser, "invalid column in order siblings by");
        }
          for (j = 0, regu_list2 = connect_by_xasl->outptr_list->valptrp; regu_list2;
           regu_list2 = regu_list2->next, j++)
        {
          if (regu_list2->value.type == TYPE_CONSTANT
              && regu_list1->value.value.dbvalptr == regu_list2->value.value.dbvalptr)
            {
              orderby->pos_descr.pos_no = j;
              break;
            }
        }
          break;
        }
    }
    }

  return xasl->orderby_list;
}

/*
 * pt_agg_orderby_to_sort_list() - Translates a list of order by PT_SORT_SPEC
 *                 nodes from a aggregate function to a XASL
 *                 SORT_LIST list
 *
 *   return: newly created XASL SORT_LIST
 *   parser(in): parser context
 *   order_list(in): list of PT_SORT_SPEC nodes
 *   agg_args_list(in): list of aggregate function arguments
 *
 *  Note : Code is similar to 'pt_to_sort_list', but tweaked for ORDERBY's for
 *     aggregate functions.
 *     Although the existing single aggregate supporting ORDER BY, allows
 *     only one ORDER BY item, this functions handles the general case of
 *     multiple ORDER BY items. However, it doesn't handle the 'hidden'
 *     argument case (see 'pt_to_sort_list'), so it may require extension
 *     in order to support multiple ORDER BY items.
 */
static SORT_LIST *
pt_agg_orderby_to_sort_list (PARSER_CONTEXT * parser, PT_NODE * order_list, PT_NODE * agg_args_list)
{
  SORT_LIST *sort_list = NULL;
  SORT_LIST *sort = NULL;
  SORT_LIST *lastsort = NULL;
  PT_NODE *node = NULL;
  PT_NODE *arg = NULL;
  int i, k;

  i = 0;            /* SORT_LIST pos_no start from 0 */

  for (node = order_list; node != NULL; node = node->next)
    {
      /* safe guard: invalid parse tree */
      if (node->node_type != PT_SORT_SPEC || node->info.sort_spec.expr == NULL)
    {
      regu_set_error_with_zero_args (ER_REGU_SYSTEM);
      return NULL;
    }

      /* check for end-of-sort */
      if (node->info.sort_spec.pos_descr.pos_no <= 0)
    {
      /* internal error */
      if (!pt_has_error (parser))
        {
          PT_INTERNAL_ERROR (parser, "generate order_by");
        }
      return NULL;
    }

      /* check for domain info */
      if (TP_DOMAIN_TYPE (node->info.sort_spec.pos_descr.dom) == DB_TYPE_NULL)
    {
      /* get domain from corresponding column node */
      for (arg = agg_args_list, k = 1; arg; arg = arg->next, k++)
        {
          if (node->info.sort_spec.pos_descr.pos_no == k)
        {
          break;    /* match */
        }
        }

      if (arg != NULL && arg->type_enum != PT_TYPE_NONE)
        {           /* is resolved */
          node->info.sort_spec.pos_descr.dom = pt_xasl_node_to_domain (parser, arg);
        }

      /* still no domain ? -> internal error */
      if (node->info.sort_spec.pos_descr.dom == NULL)
        {
          if (!pt_has_error (parser))
        {
          PT_INTERNAL_ERROR (parser, "generate order_by");
        }
          return NULL;
        }
    }

      regu_alloc (sort);
      if (!sort)
    {
      regu_set_error_with_zero_args (ER_REGU_SYSTEM);
      return NULL;
    }

      /* set values */
      sort->s_order = (node->info.sort_spec.asc_or_desc == PT_ASC) ? S_ASC : S_DESC;
      sort->s_nulls = pt_to_null_ordering (node);
      sort->pos_descr = node->info.sort_spec.pos_descr;

      /* PT_SORT_SPEC pos_no start from 1, SORT_LIST pos_no start from 0 */
      sort->pos_descr.pos_no--;
      assert (sort->pos_descr.pos_no >= 0);

      /* link up */
      if (sort_list)
    {
      lastsort->next = sort;
    }
      else
    {
      sort_list = sort;
    }

      lastsort = sort;
    }

  return sort_list;
}

/*
 * pt_find_oid_scan_block () -
 *   return:       the XASL node or NULL
 *   xasl (in):    the beginning of the XASL chain
 *   oi (in):      the OID we're looking for
 *
 *   note: in trying to optimize a general query (multiple tables, joins etc.)
 *         for using (index) keylimit for "ORDER BY ... LIMIT n" queries,
 *         we need to gather information that's scattered around the generated
 *         XASL blocks and the plan tree that was selected by the optimizer,
 *         and was used to generate the afore mentioned XASL.
 *         This method acts as a "link": it connects an xasl block with
 *         the (sub?) plan that generated it.
 */
static XASL_NODE *
pt_find_oid_scan_block (XASL_NODE * xasl, OID * oid)
{
  for (; xasl; xasl = xasl->scan_ptr)
    {
      /* only check required condition: OID match. Other, more sophisticated conditions should be checked from the
       * caller */
      if (xasl->spec_list && xasl->spec_list->indexptr && OID_EQ (&xasl->spec_list->indexptr->class_oid, oid))
    {
      return xasl;
    }
    }
  return NULL;
}

/*
 * pt_ordbynum_to_key_limit_multiple_ranges () - add key limit to optimize
 *                       index access with multiple
 *                       key ranges
 *
 *   return     : NO_ERROR if key limit is generated successfully, ER_FAILED
 *        otherwise
 *   parser(in) : parser context
 *   plan(in)   : root plan (must support multi range key limit optimization)
 *   xasl(in)   : xasl node
 */
static int
pt_ordbynum_to_key_limit_multiple_ranges (PARSER_CONTEXT * parser, QO_PLAN * plan, XASL_NODE * xasl)
{
  QO_LIMIT_INFO *limit_infop;
  QO_PLAN *subplan = NULL;
  XASL_NODE *scan = NULL;
  int ret = 0;

  if (!plan)            /* simple plan, nothing to do */
    {
      goto error_exit;
    }

  if (!xasl || !xasl->spec_list)
    {
      goto error_exit;
    }

  if (!xasl->orderby_list || !xasl->ordbynum_pred)
    {
      goto error_exit;
    }

  /* find the subplan with multiple key range */
  if (qo_find_subplan_using_multi_range_opt (plan, &subplan, NULL) != NO_ERROR)
    {
      goto error_exit;
    }
  if (subplan == NULL)
    {
      goto error_exit;
    }

  scan = pt_find_oid_scan_block (xasl, &(subplan->plan_un.scan.index->head->class_->oid));
  if (scan == NULL)
    {
      goto error_exit;
    }

  /* check that we have index scan */
  if (scan->spec_list->type != TARGET_CLASS || scan->spec_list->access != ACCESS_METHOD_INDEX
      || !scan->spec_list->indexptr)
    {
      goto error_exit;
    }

  /* no data filter */
  if (scan->spec_list->where_pred)
    {
      goto error_exit;
    }

  /* generate key limit expression from limit/ordbynum */
  limit_infop = qo_get_key_limit_from_ordbynum (parser, plan, xasl, true);
  if (!limit_infop)
    {
      goto error_exit;
    }

  /* set an auto-resetting key limit for the iscan */
  ret = pt_to_key_limit (parser, NULL, limit_infop, &scan->spec_list->indexptr->key_info, true);
  db_private_free (NULL, limit_infop);

  if (ret != NO_ERROR)
    {
      goto error_exit;
    }

  return NO_ERROR;

error_exit:
  assert (0);
  PT_INTERNAL_ERROR (parser, "Error generating key limit for multiple range \
                 key limit optimization");
  return ER_FAILED;
}

/*
 * pt_to_pos_descr_groupby () - Translate PT_SORT_SPEC node to
 *              QFILE_TUPLE_VALUE_POSITION node
 *   return:
 *   parser(in):
 *   pos_p(out):
 *   node(in):
 *   root(in):
 */
void
pt_to_pos_descr_groupby (PARSER_CONTEXT * parser, QFILE_TUPLE_VALUE_POSITION * pos_p, PT_NODE * node, PT_NODE * root)
{
  PT_NODE *temp;
  char *node_str = NULL;
  int i;

  pos_p->pos_no = -1;       /* init */
  pos_p->dom = NULL;        /* init */

  switch (root->node_type)
    {
    case PT_SELECT:
      i = 1;            /* PT_SORT_SPEC pos_no start from 1 */

      if (node->node_type == PT_EXPR)
    {
      unsigned int save_custom;

      save_custom = parser->custom_print;   /* save */
      parser->custom_print |= PT_CONVERT_RANGE;

      node_str = parser_print_tree (parser, node);

      parser->custom_print = save_custom;   /* restore */
    }

      for (temp = root->info.query.q.select.group_by; temp != NULL; temp = temp->next)
    {
      PT_NODE *expr = NULL;
      if (temp->node_type != PT_SORT_SPEC)
        {
          continue;
        }

      expr = temp->info.sort_spec.expr;

      if (node->node_type == PT_NAME)
        {
          if (pt_name_equal (parser, expr, node))
        {
          pos_p->pos_no = i;
        }
        }
      else if (node->node_type == PT_EXPR)
        {
          if (pt_str_compare (node_str, parser_print_tree (parser, expr), CASE_INSENSITIVE) == 0)
        {
          pos_p->pos_no = i;
        }
        }
      else
        {           /* node type must be an integer */
          if (node->info.value.data_value.i == i)
        {
          pos_p->pos_no = i;
        }
        }

      if (pos_p->pos_no != -1)
        {           /* found match */
          if (expr->type_enum != PT_TYPE_NONE && expr->type_enum != PT_TYPE_MAYBE)
        {       /* is resolved */
          pos_p->dom = pt_xasl_node_to_domain (parser, expr);
        }
          break;
        }

      i++;
    }

      break;

    case PT_UNION:
    case PT_INTERSECTION:
    case PT_DIFFERENCE:
      pt_to_pos_descr_groupby (parser, pos_p, node, root->info.query.q.union_.arg1);
      break;

    default:
      /* an error */
      break;
    }

  if (pos_p->pos_no == -1 || pos_p->dom == NULL)
    {               /* an error */
      pos_p->pos_no = -1;
      pos_p->dom = NULL;
    }
}

/*
 * pt_numbering_set_continue_post () - set PT_PRED_ARG_INSTNUM_CONTINUE,
 * PT_PRED_ARG_GRBYNUM_CONTINUE and PT_PRED_ARG_ORDBYNUM_CONTINUE flag
 * for numbering node
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in/out):
 *   continue_walk(in/out):
 */
PT_NODE *
pt_numbering_set_continue_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE *child = NULL;
  int *flagp = (int *) arg;
  PT_NODE *children[3];
  int i;

  if (!node)
    {
      return NULL;
    }

  if (node->node_type == PT_EXPR && node->type_enum != PT_TYPE_LOGICAL)
    {
      children[0] = node->info.expr.arg1;
      children[1] = node->info.expr.arg2;
      children[2] = node->info.expr.arg3;

      for (i = 0; i < 3; i++)
    {
      child = children[i];
      if (child
          && ((child->node_type == PT_FUNCTION && child->info.function.function_type == PT_GROUPBY_NUM)
          || (child->node_type == PT_EXPR && PT_IS_NUMBERING_AFTER_EXECUTION (child->info.expr.op))))
        {
          /* we have a subexpression with numbering functions and we don't have a logical operator therefore we set
           * the continue flag to ensure we treat all values in the pred evaluation */
          *flagp |= PT_PRED_ARG_INSTNUM_CONTINUE;
          *flagp |= PT_PRED_ARG_GRBYNUM_CONTINUE;
          *flagp |= PT_PRED_ARG_ORDBYNUM_CONTINUE;
        }
    }
    }

  return node;
}

/*
 * pt_to_analytic_node () - build analytic node
 *   return: NULL if error, input tree otherwise
 *   parser(in): parser context
 *   tree(in): input analytic node
 *   analytic_info(in/out): analytic info structure (will be altered)
 */
static PT_NODE *
pt_to_analytic_node (PARSER_CONTEXT * parser, PT_NODE * tree, ANALYTIC_INFO * analytic_info)
{
  ANALYTIC_TYPE *analytic;
  PT_FUNCTION_INFO *func_info;
  PT_NODE *list = NULL, *order_list = NULL, *link = NULL;
  PT_NODE *sort_list, *list_entry;
  PT_NODE *arg_list = NULL;
  PT_NODE *percentile = NULL;

  if (parser == NULL || analytic_info == NULL)
    {
      /* should not get here */
      assert (false);
      return tree;
    }

  if (tree == NULL || !PT_IS_ANALYTIC_NODE (tree))
    {
      /* nothing to do */
      return tree;
    }

  /* allocate analytic structure */
  regu_alloc (analytic);
  if (!analytic)
    {
      PT_ERROR (parser, tree,
        msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      goto exit_on_error;
    }

  /* link structure to analytic list */
  analytic->next = analytic_info->head_list;
  analytic_info->head_list = analytic;

  /* retrieve function info */
  func_info = &tree->info.function;

  /* fill in analytic info */
  analytic->function = func_info->function_type;
  analytic->option = (func_info->all_or_distinct == PT_ALL) ? Q_ALL : Q_DISTINCT;
  analytic->domain = pt_xasl_node_to_domain (parser, tree);
  analytic->value = (DB_VALUE *) tree->etc;
  analytic->from_last = func_info->analytic.from_last;
  analytic->ignore_nulls = func_info->analytic.ignore_nulls;

  /* set value types */
  regu_dbval_type_init (analytic->value, pt_node_to_db_type (tree));
  regu_dbval_type_init (analytic->value2, pt_node_to_db_type (tree));

  /* count partitions */
  analytic->sort_prefix_size = 0;
  analytic->sort_list_size = 0;
  for (list = func_info->analytic.partition_by; list; list = list->next)
    {
      analytic->sort_prefix_size++;
      analytic->sort_list_size++;
      link = list;      /* save last node in partitions list */
    }
  for (list = func_info->analytic.order_by; list; list = list->next)
    {
      analytic->sort_list_size++;
    }

  /* link PARTITION BY and ORDER BY sort spec lists (no differentiation is needed from now on) */
  if (link != NULL)
    {
      /* we have PARTITION BY clause */
      order_list = func_info->analytic.partition_by;

      /* When arg_list is constant, ignore order by for MEDIAN, PERCENTILE_CONT and PERCENTILE_DISC */
      if (!QPROC_IS_INTERPOLATION_FUNC (analytic) || !PT_IS_CONST (func_info->arg_list))
    {
      link->next = func_info->analytic.order_by;
    }
    }
  else
    {
      /* no PARTITION BY, only ORDER BY When arg_list is constant, ignore order by for MEDIAN, PERCENTILE_CONT and
       * PERCENTILE_DISC */
      if (!QPROC_IS_INTERPOLATION_FUNC (analytic) || !PT_IS_CONST (func_info->arg_list))
    {
      order_list = func_info->analytic.order_by;
    }
    }

  /* copy sort list for later use */
  if (order_list != NULL)
    {
      sort_list = parser_copy_tree_list (parser, order_list);
      if (sort_list == NULL)
    {
      PT_INTERNAL_ERROR (parser, "copy tree");
      goto exit_on_error;
    }
    }
  else
    {
      sort_list = NULL;
    }

  list_entry = parser_new_node (parser, PT_NODE_POINTER);
  if (list_entry == NULL)
    {
      PT_INTERNAL_ERROR (parser, "alloc node");
      goto exit_on_error;
    }

  if ((order_list != NULL && sort_list == NULL) || list_entry == NULL)
    {
      PT_ERROR (parser, tree,
        msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      goto exit_on_error;
    }

  list_entry->info.pointer.node = sort_list;
  if (sort_list != NULL)
    {
      list_entry->line_number = sort_list->line_number;
      list_entry->column_number = sort_list->column_number;
    }
  if (analytic_info->sort_lists != NULL)
    {
      list_entry->next = analytic_info->sort_lists;
      analytic_info->sort_lists = list_entry;
    }
  else
    {
      analytic_info->sort_lists = list_entry;
    }

  /* find indexes of offset and default values for LEAD/LAG/NTH_VALUE */
  if (func_info->function_type == PT_LEAD || func_info->function_type == PT_LAG
      || func_info->function_type == PT_NTH_VALUE)
    {
      bool off_found = false, def_found = false;
      int idx = 0;

      for (list = analytic_info->select_list; list != NULL; list = list->next, idx++)
    {
      if (!off_found && func_info->analytic.offset->info.pointer.node == list)
        {
          analytic->offset_idx = idx;
          off_found = true;
        }

      if (!def_found && func_info->analytic.default_value->info.pointer.node == list)
        {
          analytic->default_idx = idx;
          def_found = true;
        }
    }

      if (!off_found || !def_found)
    {
      PT_INTERNAL_ERROR (parser, "invalid analytic function structure");
      goto exit_on_error;
    }
    }

  /* percentile of PERCENTILE_CONT and PERCENTILE_DISC */
  if (func_info->function_type == PT_PERCENTILE_CONT || func_info->function_type == PT_PERCENTILE_DISC)
    {
      percentile = func_info->percentile;

      if (!PT_IS_CONST (percentile))
    {
      CAST_POINTER_TO_NODE (percentile);

      percentile =
        pt_resolve_analytic_references (parser, percentile, analytic_info->select_list, analytic_info->val_list);
      if (percentile == NULL)
        {
          /* the error is set in pt_resolve_analytic_references */
          goto exit_on_error;
        }
    }

      analytic->info.percentile.percentile_reguvar =
    pt_to_regu_variable (parser, func_info->percentile, UNBOX_AS_VALUE);
      if (analytic->info.percentile.percentile_reguvar == NULL)
    {
      /* error is set in pt_to_regu_variable */
      goto exit_on_error;
    }
    }

  /* process operand (argument) */
  if (func_info->arg_list == NULL)
    {
      /* no argument (e.g. ROW_NUMBER function) */
      analytic->opr_dbtype = DB_TYPE_NULL;
      analytic->operand.type = TYPE_DBVAL;
      analytic->operand.domain = &tp_Null_domain;
      db_make_null (&analytic->operand.value.dbval);

      goto unlink_and_exit;
    }
  else if (PT_IS_POINTER_REF_NODE (func_info->arg_list))
    {
      /* fetch operand type */
      analytic->opr_dbtype = pt_node_to_db_type (func_info->arg_list->info.pointer.node);

      /* for MEDIAN and PERCENTILE functions */
      if (QPROC_IS_INTERPOLATION_FUNC (analytic))
    {
      arg_list = func_info->arg_list->info.pointer.node;
      CAST_POINTER_TO_NODE (arg_list);

      assert (arg_list != NULL);

      if (PT_IS_CONST (arg_list))
        {
          analytic->is_const_operand = true;
        }
    }

      /* resolve operand dbval_ptr */
      if (pt_resolve_analytic_references (parser, func_info->arg_list, analytic_info->select_list,
                      analytic_info->val_list) == NULL)
    {
      goto exit_on_error;
    }

      /* populate reguvar */
      analytic->operand.type = TYPE_CONSTANT;
      analytic->operand.domain = pt_xasl_node_to_domain (parser, func_info->arg_list->info.pointer.node);
      analytic->operand.value.dbvalptr = (DB_VALUE *) func_info->arg_list->etc;

      goto unlink_and_exit;
    }
  else
    {
      /* arg should be a reference pointer that was previously set by pt_expand_analytic_node () */
      PT_INTERNAL_ERROR (parser, "unprocessed analytic argument");
      goto exit_on_error;
    }

exit_on_error:
  /* error, return null */
  tree = NULL;

unlink_and_exit:
  /* unlink PARTITION BY and ORDER BY lists if necessary */
  if (link != NULL)
    {
      link->next = NULL;
      link = NULL;
    }

  return tree;
}

/*
 * pt_to_analytic_final_node () - retrieves the node that will go in the last
 *                                outptr_list of analytic processing
 *   returns: final node, NULL on error
 *   parser(in): parser context
 *   tree(in): analytic node
 *   ex_list(out): pointer to a PT_NODE list
 *   instnum_flag(out): see NOTE2
 *
 * NOTE: This function has the following behavior:
 *
 *   1. When it receives an analytic function node, it  will put it in ex_list
 *   and will return a reference PT_NODE_POINTER to the node.
 *
 *   2. When it receives an expression containing analytic functions, it will
 *   put all analytic nodes AND subexpressions that DO NOT contain analytic
 *   nodes into the "ex_list". The function will return a PT_EXPR tree of
 *   reference PT_POINTERs.
 *
 * The returned node should be used in the "final" outptr_list of analytics
 * processing.
 *
 * NOTE2: The function will set the XASL_INSTNUM_FLAG_SELECTS_INSTNUM bit in
 * instnum_flag if an INST_NUM() is found in tree.
 */
static PT_NODE *
pt_to_analytic_final_node (PARSER_CONTEXT * parser, PT_NODE * tree, PT_NODE ** ex_list, int *instnum_flag)
{
  PT_NODE *ptr;

  if (parser == NULL || ex_list == NULL)
    {
      /* should not get here */
      assert (false);
      return tree;
    }

  if (tree == NULL)
    {
      /* nothing to do */
      return NULL;
    }

  if (PT_IS_VALUE_NODE (tree))
    {
      return tree;
    }

  if (PT_IS_ANALYTIC_NODE (tree))
    {
      /* select ntile(select stddev(...)...)... from ... is allowed */
      if (!pt_is_query (tree->info.function.arg_list) && pt_has_analytic (parser, tree->info.function.arg_list))
    {
      PT_ERRORm (parser, tree, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_NESTED_AGGREGATE);
      return NULL;
    }

      /* analytics go to ex_list, ref pointer is returned */
      goto exit_return_ptr;
    }

  if (PT_IS_EXPR_NODE (tree))
    {
      PT_NODE *ret = NULL;

      if (PT_IS_ORDERBYNUM (tree))
    {
      /* orderby_num() should be evaluated at the write of output */
      return tree;
    }

      if (PT_IS_INSTNUM (tree))
    {
      /* inst_num() should be evaluated at the write of output; also set flag so we defer inst_num() incrementation
       * to output */
      (*instnum_flag) |= XASL_INSTNUM_FLAG_EVAL_DEFER;
      return tree;
    }

      if (!pt_has_analytic (parser, tree) && !pt_has_inst_or_orderby_num (parser, tree))
    {
      /* no reason to split this expression tree, we can evaluate it in the initial scan */
      goto exit_return_ptr;
    }

      /* expression tree with analytic children; walk arguments */
      if (tree->info.expr.arg1 != NULL)
    {
      ret = pt_to_analytic_final_node (parser, tree->info.expr.arg1, ex_list, instnum_flag);
      if (ret != NULL)
        {
          tree->info.expr.arg1 = ret;
        }
      else
        {
          return NULL;
        }
    }

      if (tree->info.expr.arg2 != NULL)
    {
      ret = pt_to_analytic_final_node (parser, tree->info.expr.arg2, ex_list, instnum_flag);
      if (ret != NULL)
        {
          tree->info.expr.arg2 = ret;
        }
      else
        {
          return NULL;
        }
    }

      if (tree->info.expr.arg3 != NULL)
    {
      ret = pt_to_analytic_final_node (parser, tree->info.expr.arg3, ex_list, instnum_flag);
      if (ret != NULL)
        {
          tree->info.expr.arg3 = ret;
        }
      else
        {
          return NULL;
        }
    }

      /* we're left with final part of expression */
      return tree;
    }

  if (PT_IS_FUNCTION (tree) && pt_has_analytic (parser, tree))
    {
      PT_NODE *ret = NULL, *arg, *save_next;

      /* function with analytic arguments */
      arg = tree->info.function.arg_list;
      tree->info.function.arg_list = NULL;

      while (arg != NULL)
    {
      save_next = arg->next;
      arg->next = NULL;

      /* get final node */
      ret = pt_to_analytic_final_node (parser, arg, ex_list, instnum_flag);
      if (ret == NULL)
        {
          /* error was set */
          parser_free_tree (parser, arg);
          parser_free_tree (parser, save_next);

          return NULL;
        }

      tree->info.function.arg_list = parser_append_node (ret, tree->info.function.arg_list);

      /* advance */
      arg = save_next;
    }

      /* we're left with function with PT_NODE_POINTER arguments */
      return tree;
    }

exit_return_ptr:

  /* analytic functions, subexpressions without analytic functions and other nodes go to the ex_list */
  ptr = pt_point_ref (parser, tree);
  if (ptr == NULL)
    {
      /* allocation failed */
      PT_ERROR (parser, tree,
        msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      return NULL;
    }

  *ex_list = parser_append_node (tree, *ex_list);

  return ptr;
}

/*
 * pt_expand_analytic_node () - allocate value for result and set etc
 *   returns: NULL on error, original node otherwise
 *   parser(in): parser context
 *   node(in): analytic node
 *   select_list(in/out): select list to work on
 *
 * NOTE: this function alters the select list!
 */
static PT_NODE *
pt_expand_analytic_node (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * select_list)
{
  PT_NODE *spec, *arg, *ptr;
  PT_NODE *old_ex_list = NULL, *new_ex_list = NULL;
  PT_NODE *last_node = NULL;
  bool visited_part = false;

  if (parser == NULL)
    {
      /* should not get here */
      assert (false);
      return NULL;
    }

  if (node == NULL || !PT_IS_ANALYTIC_NODE (node))
    {
      /* nothing to do */
      return node;
    }

  if (select_list == NULL)
    {
      PT_INTERNAL_ERROR (parser, "null select list for analytic expansion");
      return NULL;
    }

  /* add argument to select list */
  arg = node->info.function.arg_list;
  node->info.function.arg_list = NULL;

  if (arg != NULL)
    {
      if (arg->next != NULL)
    {
      /* more than one argument; not allowed */
      parser_free_tree (parser, arg);

      PT_INTERNAL_ERROR (parser, "multiple args for analytic function");
      return NULL;
    }

      /* add a pointer to the node to argument list */
      ptr = pt_point_ref (parser, arg);
      if (ptr == NULL)
    {
      /* allocation failed */
      parser_free_tree (parser, arg);

      PT_ERROR (parser, node,
            msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      return NULL;
    }

      node->info.function.arg_list = ptr;

      /* add node to select list (select list is considered to be not null) */
      (void) parser_append_node (arg, select_list);
    }

  if (node->info.function.function_type == PT_LEAD || node->info.function.function_type == PT_LAG
      || node->info.function.function_type == PT_NTH_VALUE)
    {
      /* add offset and default value expressions to select list */
      ptr = pt_point_ref (parser, node->info.function.analytic.offset);
      if (ptr == NULL)
    {
      PT_ERROR (parser, node,
            msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      return NULL;
    }
      (void) parser_append_node (node->info.function.analytic.offset, select_list);
      node->info.function.analytic.offset = ptr;

      ptr = pt_point_ref (parser, node->info.function.analytic.default_value);
      if (ptr == NULL)
    {
      PT_ERROR (parser, node,
            msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      return NULL;
    }
      (void) parser_append_node (node->info.function.analytic.default_value, select_list);
      node->info.function.analytic.default_value = ptr;
    }

  /* percentile for PERCENTILE_CONT and PERCENTILE_DISC */
  if ((node->info.function.function_type == PT_PERCENTILE_CONT
       || node->info.function.function_type == PT_PERCENTILE_DISC) && !pt_is_const (node->info.function.percentile))
    {
      ptr = pt_find_name (parser, node->info.function.percentile, select_list);
      /* add percentile expression to select list */
      if (ptr == NULL)
    {
      ptr = pt_point_ref (parser, node->info.function.percentile);
      parser_append_node (node->info.function.percentile, select_list);
    }
      else
    {
      ptr = pt_point_ref (parser, ptr);
    }

      if (ptr == NULL)
    {
      PT_ERROR (parser, node,
            msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      return NULL;
    }

      node->info.function.percentile = ptr;
    }

  if (node->info.function.analytic.adjusted)
    {
      /* if old expanded list existed, append to the select list */
      if (node->info.function.analytic.expanded_list != NULL)
    {
      old_ex_list = parser_copy_tree_list (parser, node->info.function.analytic.expanded_list);
      if (old_ex_list == NULL)
        {
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return NULL;
        }
      (void) parser_append_node (old_ex_list, select_list);
    }

      return node;
    }

  /* get the last node of select_list */
  last_node = select_list;
  while (last_node->next != NULL)
    {
      last_node = last_node->next;
    }

  /* walk order list and resolve nodes that were not found in select list */
  spec = node->info.function.analytic.partition_by;
  if (spec == NULL)
    {
      spec = node->info.function.analytic.order_by;
      visited_part = true;
    }

  while (spec)
    {
      PT_NODE *val = NULL, *expr = NULL, *list = select_list, *last = NULL;
      int pos = 1;      /* sort spec indexing starts from 1 */

      if (spec->node_type != PT_SORT_SPEC)
    {
      PT_INTERNAL_ERROR (parser, "invalid sort spec");
      return NULL;
    }

      /* pull sort expression */
      expr = spec->info.sort_spec.expr;
      if (expr == NULL)
    {
      PT_INTERNAL_ERROR (parser, "null sort expression");
      return NULL;
    }

      if (expr->node_type != PT_VALUE)
    {
      bool found = false;

      /* we have an actual expression; move it in the select list and put a position value here */
      while (list != NULL)
        {
          if ((list->node_type == PT_NAME || list->node_type == PT_DOT_)
          && (expr->node_type == PT_NAME || expr->node_type == PT_DOT_))
        {
          if (pt_check_path_eq (parser, list, expr) == 0)
            {
              found = true;
              break;
            }
        }

          last = list;
          list = list->next;
          pos++;
        }

      if (!found)
        {
          /* no match, add it in select list */
          last->next = expr;
        }

      /* unlink from sort spec */
      spec->info.sort_spec.expr = NULL;

      /* create new value spec */
      val = parser_new_node (parser, PT_VALUE);
      if (val == NULL)
        {
          PT_ERRORm (parser, spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return NULL;
        }

      val->type_enum = PT_TYPE_INTEGER;
      val->info.value.data_value.i = pos;
      (void) pt_value_to_db (parser, val);

      /* set value spec and position descriptor */
      spec->info.sort_spec.expr = val;
      spec->info.sort_spec.pos_descr.pos_no = pos;

      /* resolve domain */
      if (expr->type_enum != PT_TYPE_NONE && expr->type_enum != PT_TYPE_MAYBE)
        {
          spec->info.sort_spec.pos_descr.dom = pt_xasl_node_to_domain (parser, expr);
        }

      if (found)
        {
          /* cleanup */
          parser_free_tree (parser, expr);
        }
    }

      /* advance */
      spec = spec->next;
      if (spec == NULL && !visited_part)
    {
      spec = node->info.function.analytic.order_by;
      visited_part = true;
    }
    }

  /* Since the partition_by and order_by may be replaced as pt_value, the old expr should be reserved. */
  if (last_node->next != NULL)
    {
      new_ex_list = parser_copy_tree_list (parser, last_node->next);
      if (new_ex_list == NULL)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      return NULL;
    }

      assert (node->info.function.analytic.expanded_list == NULL);
      node->info.function.analytic.expanded_list = new_ex_list;
    }

  /* set the analytic has been adjusted and expanded */
  node->info.function.analytic.adjusted = true;

  /* all ok */
  return node;
}

/*
 * pt_set_analytic_node_etc () - allocate value for result and set etc
 *   returns: NULL on error, input node otherwise
 *   parser(in): parser context
 *   node(in): analytic input node
 */
static PT_NODE *
pt_set_analytic_node_etc (PARSER_CONTEXT * parser, PT_NODE * node)
{
  DB_VALUE *value;

  if (parser == NULL)
    {
      /* should not get here */
      assert (false);
      return node;
    }

  if (node == NULL || !PT_IS_ANALYTIC_NODE (node))
    {
      /* nothing to do */
      return node;
    }

  /* allocate DB_VALUE and store it in etc */
  regu_alloc (value);
  if (value == NULL)
    {
      PT_ERROR (parser, node,
        msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      return NULL;
    }

  regu_dbval_type_init (value, DB_TYPE_NULL);
  node->etc = (void *) value;

  /* all ok */
  return node;
}

/*
 * pt_adjust_analytic_sort_specs () - adjust analytic sort spec indices
 *   parser(in): parser context
 *   node(in): analytic node
 *   idx(in): index of analytic node in list
 *   adjust(in): amount to adjust for
 */
static void
pt_adjust_analytic_sort_specs (PARSER_CONTEXT * parser, PT_NODE * node, int idx, int adjust)
{
  PT_NODE *spec;

  if (parser == NULL)
    {
      /* should not get here */
      assert (false);
      return;
    }

  if (node == NULL || !PT_IS_ANALYTIC_NODE (node))
    {
      /* nothing to do */
      return;
    }

  if (node->info.function.analytic.adjusted)
    {
      /* nothing to do */
      return;
    }

  /* walk sort specs and adjust */
  for (spec = node->info.function.analytic.order_by; spec; spec = spec->next)
    {
      if (!PT_IS_SORT_SPEC_NODE (spec) || !PT_IS_VALUE_NODE (spec->info.sort_spec.expr))
    {
      /* nothing to process */
      continue;
    }

      if (spec->info.sort_spec.pos_descr.pos_no > idx)
    {
      /* should be adjusted */
      spec->info.sort_spec.pos_descr.pos_no += adjust;
      spec->info.sort_spec.expr->info.value.data_value.i += adjust;
      spec->info.sort_spec.expr->info.value.db_value.data.i += adjust;
    }
    }

  for (spec = node->info.function.analytic.partition_by; spec; spec = spec->next)
    {
      if (!PT_IS_SORT_SPEC_NODE (spec) || !PT_IS_VALUE_NODE (spec->info.sort_spec.expr))
    {
      /* nothing to process */
      continue;
    }

      if (spec->info.sort_spec.pos_descr.pos_no > idx)
    {
      /* should be adjusted */
      spec->info.sort_spec.pos_descr.pos_no += adjust;
      spec->info.sort_spec.expr->info.value.data_value.i += adjust;
      spec->info.sort_spec.expr->info.value.db_value.data.i += adjust;
    }
    }
}

/*
 * pt_resolve_analytic_references () - resolve reference pointers to DB_VALUE
 *                                     pointers of vallist
 *   returns: NULL on error, original node otherwise
 *   parser(in): parser context
 *   node(in): node tree to resolve
 *   select_list(in): select list to resolve to
 *   vallist(in): value list
 *
 * NOTE: this function will look up in the select list any reference pointers
 * and will set the pointer's "etc" to the corresponding DB_VALUE in the
 * vallist.
 */
static PT_NODE *
pt_resolve_analytic_references (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * select_list, VAL_LIST * vallist)
{
  if (parser == NULL)
    {
      /* should not get here */
      assert (false);
      return node;
    }

  if (node == NULL)
    {
      /* nothing to do */
      return node;
    }

  if (PT_IS_POINTER_REF_NODE (node))
    {
      PT_NODE *real_node = node->info.pointer.node;
      PT_NODE *list = select_list;
      QPROC_DB_VALUE_LIST db_list = vallist->valp;

      /* get real node pointer */
      CAST_POINTER_TO_NODE (real_node);

      /* look it up in select list */
      for (; list && db_list; list = list->next, db_list = db_list->next)
    {
      if (list == real_node)
        {
          /* found */
          node->etc = (void *) db_list->val;
          return node;
        }
    }

      if (list == NULL)
    {
      PT_INTERNAL_ERROR (parser, "pointed node not found in select list");
      return NULL;
    }
      else
    {
      PT_INTERNAL_ERROR (parser, "invalid size of vallist");
      return NULL;
    }
    }
  else if (PT_IS_EXPR_NODE (node))
    {
      /* resolve expression arguments */
      if (node->info.expr.arg1
      && pt_resolve_analytic_references (parser, node->info.expr.arg1, select_list, vallist) == NULL)
    {
      return NULL;
    }

      if (node->info.expr.arg2
      && pt_resolve_analytic_references (parser, node->info.expr.arg2, select_list, vallist) == NULL)
    {
      return NULL;
    }

      if (node->info.expr.arg3
      && pt_resolve_analytic_references (parser, node->info.expr.arg3, select_list, vallist) == NULL)
    {
      return NULL;
    }
    }
  else if (PT_IS_FUNCTION (node))
    {
      PT_NODE *arg;

      /* resolve function arguments */
      for (arg = node->info.function.arg_list; arg; arg = arg->next)
    {
      if (pt_resolve_analytic_references (parser, arg, select_list, vallist) == NULL)
        {
          return NULL;
        }
    }
    }

  /* all ok */
  return node;
}

/*
 * pt_substitute_analytic_references () - substitute reference pointers to normal
 *                                        nodes
 *  return: processed node
 *   parser(in): parser context
 *   node(in): node tree to resolve
 */
static PT_NODE *
pt_substitute_analytic_references (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE ** ex_list)
{
  if (parser == NULL)
    {
      /* should not get here */
      assert (false);
      return node;
    }

  if (node == NULL)
    {
      /* nothing to do */
      return node;
    }

  if (PT_IS_VALUE_NODE (node))
    {
      return node;
    }

  if (PT_IS_POINTER_REF_NODE (node))
    {
      PT_NODE *real_node = node->info.pointer.node;
      PT_NODE *list, *prev = NULL;

      /* unlink from extended list */
      for (list = *ex_list; list; list = list->next)
    {
      if (list == real_node)
        {
          if (prev != NULL)
        {
          prev->next = real_node->next;
        }
          else
        {
          /* first node in list */
          *ex_list = real_node->next;
        }

          break;
        }

      /* keep previous */
      prev = list;
    }

      /* unlink real node */
      real_node->next = NULL;

      /* dispose of this node */
      node->info.pointer.node = NULL;
      parser_free_node (parser, node);

      /* return real node */
      return real_node;
    }
  else if (PT_IS_EXPR_NODE (node))
    {
      PT_NODE *ret;

      /* walk expression arguments */
      if (node->info.expr.arg1)
    {
      ret = pt_substitute_analytic_references (parser, node->info.expr.arg1, ex_list);
      if (ret != NULL)
        {
          node->info.expr.arg1 = ret;
        }
      else
        {
          return NULL;
        }
    }

      if (node->info.expr.arg2)
    {
      ret = pt_substitute_analytic_references (parser, node->info.expr.arg2, ex_list);
      if (ret != NULL)
        {
          node->info.expr.arg2 = ret;
        }
      else
        {
          return NULL;
        }
    }

      if (node->info.expr.arg3)
    {
      ret = pt_substitute_analytic_references (parser, node->info.expr.arg3, ex_list);
      if (ret != NULL)
        {
          node->info.expr.arg3 = ret;
        }
      else
        {
          return NULL;
        }
    }

      return node;
    }
  else if (PT_IS_FUNCTION (node))
    {
      PT_NODE *prev = NULL;
      for (PT_NODE * arg = node->info.function.arg_list; arg != NULL; arg = arg->next)
    {
      PT_NODE *save_next = arg->next;

      PT_NODE *ret = pt_substitute_analytic_references (parser, arg, ex_list);
      if (ret == NULL)
        {
          /* error has been set */
          parser_free_tree (parser, arg);
          parser_free_tree (parser, save_next);

          return NULL;
        }

      if (arg != ret)
        {
          if (prev != NULL)
        {
          prev->next = arg = ret;
          arg->next = save_next;
        }
          else
        {
          node->info.function.arg_list = arg = ret;
          arg->next = save_next;
        }
        }

      prev = arg;
    }

      return node;
    }
  else
    {
      PT_INTERNAL_ERROR (parser, "invalid node type");
      return NULL;
    }
}

/*
 * pt_get_pred_attrs () - get index filter predicate attributtes
 *   return: error code
 *   table_info(in): table info
 *   pred(in): filter predicate parse tree
 *   pred_attrs(in/out): filter predicate attributes
 */
static int
pt_get_pred_attrs (PARSER_CONTEXT * parser, TABLE_INFO * table_info, PT_NODE * pred, PT_NODE ** pred_attrs)
{
  PT_NODE *tmp = NULL, *pointer = NULL, *real_attrs = NULL;
  PT_NODE *pred_nodes = NULL;
  PT_NODE *node = NULL, *save_node = NULL, *save_next = NULL, *ref_node = NULL;

  /* TO DO : check memory allocation */
  if (pred_attrs == NULL || table_info == NULL || table_info->class_spec == NULL)
    {
      return ER_FAILED;
    }

  *pred_attrs = NULL;
  /* mq_get_references() is destructive to the real set of referenced attrs, so we need to squirrel it away. */
  real_attrs = table_info->class_spec->info.spec.referenced_attrs;
  table_info->class_spec->info.spec.referenced_attrs = NULL;

  /* Traverse pred */
  for (node = pred; node; node = node->next)
    {
      save_node = node;     /* save */

      CAST_POINTER_TO_NODE (node);

      if (node)
    {
      /* save and cut-off node link */
      save_next = node->next;
      node->next = NULL;

      ref_node = mq_get_references (parser, node, table_info->class_spec);
      pred_nodes = parser_append_node (ref_node, pred_nodes);

      /* restore node link */
      node->next = save_next;
    }

      node = save_node;     /* restore */
    }               /* for (node = ...) */

  table_info->class_spec->info.spec.referenced_attrs = real_attrs;

  tmp = pred_nodes;
  while (tmp)
    {
      pointer = pt_point (parser, tmp);
      if (pointer == NULL)
    {
      goto exit_on_error;
    }

      *pred_attrs = parser_append_node (pointer, *pred_attrs);
      tmp = tmp->next;
    }

  return NO_ERROR;

exit_on_error:

  if (*pred_attrs)
    {
      parser_free_tree (parser, *pred_attrs);
    }
  if (pred_nodes)
    {
      parser_free_tree (parser, pred_nodes);
    }

  return ER_FAILED;
}

/*
 * pt_to_func_pred () - converts an expression parse tree to a
 *                  FUNC_PRED structure
 *   return:
 *   parser(in):
 *   spec (in) :
 *   expr(in):
 */
FUNC_PRED *
pt_to_func_pred (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * expr)
{
  DB_OBJECT *class_obj = NULL;
  MOBJ class_ = NULL;
  HFID *hfid = NULL;
  FUNC_PRED *func_pred = NULL;
  SYMBOL_INFO *symbols = NULL;
  TABLE_INFO *table_info = NULL;

  if (parser == NULL || expr == NULL || spec == NULL)
    {
      return NULL;
    }
  if ((parser->symbols = pt_symbol_info_alloc ()) == NULL)
    {
      goto outofmem;
    }
  symbols = parser->symbols;

  symbols->table_info = pt_make_table_info (parser, spec);
  if (symbols->table_info == NULL)
    {
      goto outofmem;
    }

  table_info = symbols->table_info;
  /* should be only one node in flat_entity_list */
  symbols->current_class = spec->info.spec.flat_entity_list;
  regu_alloc (symbols->cache_attrinfo);
  if (symbols->cache_attrinfo == NULL)
    {
      goto outofmem;
    }
  table_info->class_spec = spec;
  table_info->exposed = spec->info.spec.range_var->info.name.original;
  table_info->spec_id = spec->info.spec.id;
  /* don't care about attribute_list and value_list */
  table_info->attribute_list = NULL;
  table_info->value_list = NULL;

  /* flush the class, just to be sure */
  class_obj = spec->info.spec.flat_entity_list->info.name.db_object;
  class_ = locator_create_heap_if_needed (class_obj, sm_is_reuse_oid_class (class_obj));
  if (class_ == NULL)
    {
      return NULL;
    }

  hfid = sm_ch_heap (class_);
  if (hfid == NULL)
    {
      return NULL;
    }

  regu_alloc (func_pred);
  if (func_pred)
    {
      func_pred->func_regu = pt_to_regu_variable (parser, expr, UNBOX_AS_VALUE);
      func_pred->cache_attrinfo = symbols->cache_attrinfo;
    }

  if (pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_SEMANTIC);
      func_pred = NULL;
    }

  return func_pred;

outofmem:
  PT_ERRORm (parser, spec, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);

  return NULL;
}

/*
 * validate_regu_key_function_index () - checks if a regu variable can be used
 *                   as key information when the index scan
 *                   uses a function index.
 *   return:
 *   regu_var(in):
 */
static bool
validate_regu_key_function_index (REGU_VARIABLE * regu_var)
{
  /* if add it here, add it to pt_expr_is_allowed_as_function_index() as well */
  if (regu_var->type == TYPE_INARITH)
    {
      switch (regu_var->value.arithptr->opcode)
    {
    case T_MOD:
    case T_LEFT:
    case T_RIGHT:
    case T_REPEAT:
    case T_SPACE:
    case T_MID:
    case T_STRCMP:
    case T_REVERSE:
    case T_BIT_COUNT:
    case T_FLOOR:
    case T_CEIL:
    case T_ABS:
    case T_POWER:
    case T_ROUND:
    case T_LOG:
    case T_EXP:
    case T_SQRT:
    case T_SIN:
    case T_COS:
    case T_TAN:
    case T_COT:
    case T_ACOS:
    case T_ASIN:
    case T_ATAN:
    case T_ATAN2:
    case T_DEGREES:
    case T_DATE:
    case T_TIME:
    case T_RADIANS:
    case T_LN:
    case T_LOG2:
    case T_LOG10:
    case T_TRUNC:
    case T_CHR:
    case T_INSTR:
    case T_LEAST:
    case T_GREATEST:
    case T_POSITION:
    case T_LOWER:
    case T_UPPER:
    case T_CHAR_LENGTH:
    case T_LTRIM:
    case T_RTRIM:
    case T_FROM_UNIXTIME:
    case T_SUBSTRING_INDEX:
    case T_MD5:
    case T_AES_ENCRYPT:
    case T_AES_DECRYPT:
    case T_SHA_ONE:
    case T_SHA_TWO:
    case T_LPAD:
    case T_RPAD:
    case T_REPLACE:
    case T_TRANSLATE:
    case T_ADD_MONTHS:
    case T_LAST_DAY:
    case T_UNIX_TIMESTAMP:
    case T_STR_TO_DATE:
    case T_TIME_FORMAT:
    case T_TIMESTAMP:
    case T_YEAR:
    case T_MONTH:
    case T_DAY:
    case T_HOUR:
    case T_MINUTE:
    case T_SECOND:
    case T_QUARTER:
    case T_WEEKDAY:
    case T_DAYOFWEEK:
    case T_DAYOFYEAR:
    case T_TODAYS:
    case T_FROMDAYS:
    case T_TIMETOSEC:
    case T_SECTOTIME:
    case T_MAKEDATE:
    case T_MAKETIME:
    case T_WEEK:
    case T_MONTHS_BETWEEN:
    case T_FORMAT:
    case T_DATE_FORMAT:
    case T_ADDDATE:
    case T_DATE_ADD:
    case T_DATEDIFF:
    case T_TIMEDIFF:
    case T_SUBDATE:
    case T_DATE_SUB:
    case T_BIT_LENGTH:
    case T_OCTET_LENGTH:
    case T_IFNULL:
    case T_LOCATE:
    case T_SUBSTRING:
    case T_NVL:
    case T_NVL2:
    case T_NULLIF:
    case T_TO_CHAR:
    case T_TO_DATE:
    case T_TO_DATETIME:
    case T_TO_TIMESTAMP:
    case T_TO_TIME:
    case T_TO_NUMBER:
    case T_TRIM:
    case T_INET_ATON:
    case T_INET_NTOA:
    case T_TO_BASE64:
    case T_FROM_BASE64:
    case T_TZ_OFFSET:
    case T_TO_DATETIME_TZ:
    case T_TO_TIMESTAMP_TZ:
    case T_CRC32:
    case T_CONV_TZ:
      break;
    default:
      return true;
    }
      if (regu_var->value.arithptr->leftptr && !VALIDATE_REGU_KEY_HELPER (regu_var->value.arithptr->leftptr))
    {
      return false;
    }
      if (regu_var->value.arithptr->rightptr && !VALIDATE_REGU_KEY_HELPER (regu_var->value.arithptr->rightptr))
    {
      return false;
    }
      if (regu_var->value.arithptr->thirdptr && !VALIDATE_REGU_KEY_HELPER (regu_var->value.arithptr->thirdptr))
    {
      return false;
    }
      return true;
    }
  return true;
}

/*
 * pt_to_merge_update_query () - Creates a query for MERGE UPDATE part
 *   return: resulted query, or NULL if error
 *   parser(in): parser context
 *   select_list(in): nodes for select list
 *   info(in): MERGE statement info
 *
 * Note: Prepends the class oid and the instance oid onto the select list for
 * use during the update operation.
 * If the operation is a server side update, the prepended class oid is
 * put in the list file otherwise the class oid is a hidden column and
 * not put in the list file.
 */
PT_NODE *
pt_to_merge_update_query (PARSER_CONTEXT * parser, PT_NODE * select_list, PT_MERGE_INFO * info)
{
  PT_NODE *statement, *where, *group_by, *oid, *save_next, *arg = NULL;

  statement = parser_new_node (parser, PT_SELECT);
  if (!statement)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

  /* substitute updated columns in delete pred and add it to select list */
  if (info->update.has_delete)
    {
      PT_NODE *node, *del_search_cond;
      del_search_cond = parser_copy_tree_list (parser, info->update.del_search_cond);
      if (del_search_cond)
    {
      (void) parser_walk_tree (parser, del_search_cond, NULL, NULL, pt_substitute_assigned_name_node,
                   (void *) info->update.assignment);
    }
      else
    {
      /* delete where (true) */
      del_search_cond = pt_make_integer_value (parser, 1);
      if (del_search_cond == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          return NULL;
        }
    }

      node = parser_new_node (parser, PT_EXPR);
      if (node)
    {
      node->info.expr.op = PT_NE;
      node->info.expr.arg1 = del_search_cond;
      node->info.expr.arg2 = pt_make_integer_value (parser, 0);
      if (node->info.expr.arg2 == NULL)
        {
          parser_free_tree (parser, node);
          parser_free_tree (parser, del_search_cond);
          PT_INTERNAL_ERROR (parser, "allocate new node");
          return NULL;
        }
    }
      else
    {
      parser_free_tree (parser, del_search_cond);
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

      node->next = select_list;
      select_list = node;
    }

  /* set select list */
  statement->info.query.q.select.list = parser_copy_tree_list (parser, select_list);

  /* set spec list */
  statement->info.query.q.select.from = parser_copy_tree_list (parser, info->into);
  /* add the class and instance OIDs to the select list */
  statement = pt_add_row_classoid_name (parser, statement, (info->flags & PT_MERGE_INFO_SERVER_OP));
  statement = pt_add_row_oid_name (parser, statement);
  /* add source table to spec */
  statement->info.query.q.select.from =
    parser_append_node (parser_copy_tree_list (parser, info->using_clause), statement->info.query.q.select.from);

  /* set search condition */
  if (info->update.search_cond)
    {
      if (info->search_cond)
    {
      where = parser_new_node (parser, PT_EXPR);
      if (where)
        {
          where->info.expr.op = PT_AND;
          where->info.expr.arg1 = parser_copy_tree_list (parser, info->search_cond);
          where->info.expr.arg2 = parser_copy_tree_list (parser, info->update.search_cond);
        }
      else
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          return NULL;
        }
    }
      else
    {
      where = parser_copy_tree_list (parser, info->update.search_cond);
    }
    }
  else
    {
      where = parser_copy_tree_list (parser, info->search_cond);
    }
  statement->info.query.q.select.where = where;

  /* add group by */
  group_by = parser_new_node (parser, PT_SORT_SPEC);
  if (group_by)
    {
      oid = statement->info.query.q.select.list;
      save_next = oid->next;
      oid->next = NULL;
      group_by->info.sort_spec.expr = parser_copy_tree_list (parser, oid);
      group_by->info.sort_spec.asc_or_desc = PT_ASC;
      group_by->info.sort_spec.pos_descr.dom = pt_xasl_node_to_domain (parser, oid);
      group_by->info.sort_spec.pos_descr.pos_no = 1;
      oid->next = save_next;
      statement->info.query.q.select.group_by = group_by;
      PT_SELECT_INFO_SET_FLAG (statement, PT_SELECT_INFO_MVCC_LOCK_NEEDED);
    }
  else
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

  statement->info.query.upd_del_class_cnt = 1;
  statement->info.query.scan_op_type = S_UPDATE;
  PT_SELECT_INFO_SET_FLAG (statement, PT_SELECT_INFO_IS_MERGE_QUERY);
  PT_SELECT_INFO_SET_FLAG (statement, PT_SELECT_INFO_MULTI_UPDATE_AGG);

  /* no strict oid checking for generated subquery */
  PT_SELECT_INFO_SET_FLAG (statement, PT_SELECT_INFO_NO_STRICT_OID_CHECK);

  /* we don't need to keep this query */
  statement->flag.cannot_prepare = 1;

  /* set index hint */
  if (info->hint & PT_HINT_USE_UPDATE_IDX)
    {
      arg = info->update.index_hint;
      if (arg != NULL)
    {
      arg = parser_copy_tree_list (parser, arg);
      if (arg == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          return NULL;
        }
    }
      statement->info.query.q.select.using_index = arg;
    }

  if (info->hint & PT_HINT_NO_USE_HASH)
    {
      statement->info.query.q.select.hint |= PT_HINT_NO_USE_HASH;

      arg = info->no_use_hash;
      if (arg != NULL)
    {
      arg = parser_copy_tree_list (parser, arg);
      if (arg == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          return NULL;
        }
    }
      statement->info.query.q.select.no_use_hash = arg;
    }

  if (info->hint & PT_HINT_USE_HASH)
    {
      statement->info.query.q.select.hint |= PT_HINT_USE_HASH;

      arg = info->use_hash;
      if (arg != NULL)
    {
      arg = parser_copy_tree_list (parser, arg);
      if (arg == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          return NULL;
        }
    }
      statement->info.query.q.select.use_hash = arg;
    }

  if (PT_SELECT_INFO_IS_FLAGED (statement, PT_SELECT_INFO_MVCC_LOCK_NEEDED))
    {
      pt_mark_spec_list_for_update_clause (parser, statement, PT_SPEC_FLAG_UPDATE);
    }

  return statement;
}

/*
 * pt_to_merge_insert_query () - Creates a query for MERGE INSERT part
 *   return: resulted query, or NULL if error
 *   parser(in): parser context
 *   select_list(in): nodes for select list
 *   info(in): MERGE statement info
 *
 */
PT_NODE *
pt_to_merge_insert_query (PARSER_CONTEXT * parser, PT_NODE * select_list, PT_MERGE_INFO * info)
{
  PT_NODE *subq, *corr_subq = NULL, *expr = NULL, *and_expr = NULL, *value = NULL;

  subq = parser_new_node (parser, PT_SELECT);
  if (subq == NULL)
    {
      goto error_exit;
    }

  corr_subq = parser_new_node (parser, PT_SELECT);
  if (corr_subq == NULL)
    {
      goto error_exit;
    }

  expr = parser_new_node (parser, PT_EXPR);
  if (expr == NULL)
    {
      goto error_exit;
    }

  expr->info.expr.op = PT_NOT;
  expr->type_enum = PT_TYPE_LOGICAL;
  expr->info.expr.arg1 = parser_new_node (parser, PT_EXPR);
  if (expr->info.expr.arg1 == NULL)
    {
      goto error_exit;
    }

  expr->info.expr.arg1->info.expr.op = PT_EXISTS;
  expr->info.expr.arg1->type_enum = PT_TYPE_LOGICAL;
  expr->info.expr.arg1->info.expr.arg1 = corr_subq;

  value = parser_new_node (parser, PT_VALUE);
  if (value == NULL)
    {
      goto error_exit;
    }

  value->type_enum = PT_TYPE_INTEGER;
  value->info.value.data_value.i = 0;

  corr_subq->info.query.q.select.list = value;
  corr_subq->info.query.q.select.from = parser_copy_tree (parser, info->into);
  corr_subq->info.query.q.select.where = parser_copy_tree_list (parser, info->search_cond);
  /* add class where part */
  if (info->insert.class_where)
    {
      corr_subq->info.query.q.select.where =
    parser_append_node (parser_copy_tree_list (parser, info->insert.class_where),
                corr_subq->info.query.q.select.where);
    }

  corr_subq->info.query.q.select.flavor = PT_USER_SELECT;
  corr_subq->info.query.is_subquery = PT_IS_SUBQUERY;
  corr_subq->info.query.correlation_level = 1;
  corr_subq->info.query.flag.single_tuple = 1;

  /* set index hint */
  if (info->hint & PT_HINT_USE_INSERT_IDX)
    {
      corr_subq->info.query.q.select.using_index = parser_copy_tree_list (parser, info->insert.index_hint);
    }

  subq->info.query.q.select.list = parser_copy_tree_list (parser, select_list);
  subq->info.query.q.select.from = parser_copy_tree (parser, info->using_clause);

  if (info->insert.search_cond)
    {
      and_expr = parser_new_node (parser, PT_EXPR);
      if (and_expr == NULL)
    {
      goto error_exit;
    }

      and_expr->type_enum = PT_TYPE_LOGICAL;
      and_expr->info.expr.op = PT_AND;
      and_expr->info.expr.arg1 = expr;
      and_expr->info.expr.arg2 = parser_copy_tree_list (parser, info->insert.search_cond);

      subq->info.query.q.select.where = and_expr;
    }
  else
    {
      subq->info.query.q.select.where = expr;
    }
  PT_SELECT_INFO_SET_FLAG (subq, PT_SELECT_INFO_IS_MERGE_QUERY);

  /* we don't need to keep this query */
  subq->flag.cannot_prepare = 1;

  return subq;

error_exit:

  if (subq)
    {
      parser_free_tree (parser, subq);
    }

  if (expr)
    {
      if (expr->info.expr.arg1)
    {
      parser_free_tree (parser, expr->info.expr.arg1);
      corr_subq = NULL;
    }
      parser_free_tree (parser, expr);
    }

  if (corr_subq)
    {
      parser_free_tree (parser, corr_subq);
    }

  if (and_expr)
    {
      parser_free_tree (parser, and_expr);
    }

  if (value)
    {
      parser_free_tree (parser, value);
    }

  PT_INTERNAL_ERROR (parser, "allocate new node");
  return NULL;
}

/*
 * pt_to_merge_xasl () - Generate XASL for MERGE statement
 *   return:
 *   parser(in):
 *   statement(in):
 *   non_null_upd_attrs(in):
 *   non_null_ins_attrs(in):
 *   default_expr_attrs(in):
 */
XASL_NODE *
pt_to_merge_xasl (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE ** non_null_upd_attrs,
          PT_NODE ** non_null_ins_attrs, PT_NODE * default_expr_attrs)
{
  assert (parser != NULL && statement != NULL);

  XASL_NODE *xasl, *xptr;
  XASL_NODE *update_xasl = NULL, *insert_xasl = NULL;
  OID *oid = NULL;
  int error = NO_ERROR;
  bool insert_only = (statement->info.merge.flags & PT_MERGE_INFO_INSERT_ONLY);

  if (statement->info.merge.into && statement->info.merge.into->info.spec.remote_server_name)
    {
      return pt_to_xasl_for_dblink (parser, statement->info.merge.into);
    }

  xasl = regu_xasl_node_alloc (MERGE_PROC);
  if (xasl == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      return NULL;
    }

  if (statement->info.merge.update.assignment && !insert_only)
    {
      /* generate XASL for UPDATE part */
      update_xasl = pt_to_merge_update_xasl (parser, statement, non_null_upd_attrs);
      if (update_xasl == NULL)
    {
      return NULL;
    }
    }

  if (statement->info.merge.insert.value_clauses)
    {
      /* generate XASL for INSERT part */
      insert_xasl = pt_to_merge_insert_xasl (parser, statement, *non_null_ins_attrs, default_expr_attrs);
      if (insert_xasl == NULL)
    {
      return NULL;
    }
    }

  /* finalize XASL */
  xasl->proc.merge.update_xasl = update_xasl;
  xasl->proc.merge.insert_xasl = insert_xasl;
  xasl->proc.merge.has_delete = statement->info.merge.update.has_delete;
  xasl->query_alias = statement->alias_print;

  if ((oid = ws_identifier (db_get_user ())) != NULL)
    {
      COPY_OID (&xasl->creator_oid, oid);
    }
  else
    {
      OID_SET_NULL (&xasl->creator_oid);
    }

  /* list of class OIDs used in this XASL */
  xptr = (update_xasl ? update_xasl : insert_xasl);

  xasl->class_oid_list = regu_oid_array_alloc (xptr->n_oid_list);
  xasl->class_locks = regu_int_array_alloc (xptr->n_oid_list);
  xasl->tcard_list = regu_int_array_alloc (xptr->n_oid_list);
  if (xasl->class_oid_list == NULL || xasl->class_locks == NULL || xasl->tcard_list == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      return NULL;
    }

  xasl->n_oid_list = xptr->n_oid_list;

  /* copy xptr oids to xasl */
  (void) memcpy (xasl->class_oid_list, xptr->class_oid_list, sizeof (OID) * xptr->n_oid_list);
  (void) memcpy (xasl->class_locks, xptr->class_locks, sizeof (int) * xptr->n_oid_list);
  (void) memcpy (xasl->tcard_list, xptr->tcard_list, sizeof (int) * xptr->n_oid_list);

  /* set host variable count */
  xasl->dbval_cnt = parser->dbval_cnt;

  /* set TDE flag */
  XASL_SET_FLAG (xasl, xptr->flag & XASL_INCLUDES_TDE_CLASS);

  scan_check_parallel_heap_scan_possible (xasl);

  return xasl;
}

/*
 * pt_to_merge_update_xasl () - Generate XASL for UPDATE part of MERGE
 *   return:
 *   parser(in):
 *   statement(in):
 *   non_null_attrs(in):
 */
static XASL_NODE *
pt_to_merge_update_xasl (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE ** non_null_attrs)
{
  XASL_NODE *xasl = NULL, *aptr;
  UPDATE_PROC_NODE *update = NULL;
  UPDDEL_CLASS_INFO *upd_cls = NULL;
  PT_NODE *assigns = statement->info.merge.update.assignment;
  PT_NODE *aptr_statement = NULL;
  PT_NODE *p = NULL;
  PT_NODE *cl_name_node = NULL;
  int num_subclasses = 0;
  PT_NODE *from = NULL;
  int cl = 0, num_vals = 0, num_consts = 0;
  int error = NO_ERROR;
  int a = 0, assign_idx = 0, attr_offset = 0;
  PT_NODE *att_name_node = NULL;
  DB_VALUE *val = NULL;
  DB_ATTRIBUTE *attr = NULL;
  DB_DOMAIN *dom = NULL;
  OID *class_oid = NULL;
  DB_OBJECT *class_obj = NULL;
  HFID *hfid = NULL;
  PT_NODE *hint_arg = NULL;
  PT_ASSIGNMENTS_HELPER assign_helper;
  PT_NODE **links = NULL;
  UPDATE_ASSIGNMENT *assign = NULL;
  PT_NODE *select_names = NULL;
  PT_NODE *select_values = NULL;
  PT_NODE *const_names = NULL;
  PT_NODE *const_values = NULL;
  OID *oid = NULL;
  PT_MERGE_INFO *info = &statement->info.merge;
  PT_NODE *copy_assigns, *save_assigns;

  from = parser_copy_tree (parser, info->into);
  from = parser_append_node (parser_copy_tree_list (parser, info->using_clause), from);

  if (from == NULL || from->node_type != PT_SPEC || from->info.spec.range_var == NULL)
    {
      PT_INTERNAL_ERROR (parser, "invalid spec");
      goto cleanup;
    }

  error = pt_append_omitted_on_update_expr_assignments (parser, assigns, from);
  if (error != NO_ERROR)
    {
      PT_INTERNAL_ERROR (parser, "merge update");
      goto cleanup;
    }

  /* make a copy of assignment list to be able to iterate later */
  copy_assigns = parser_copy_tree_list (parser, info->update.assignment);

  /* get assignments lists for select statement generation */
  error =
    pt_get_assignment_lists (parser, &select_names, &select_values, &const_names, &const_values, &num_vals, &num_consts,
                 info->update.assignment, &links);
  if (error != NO_ERROR)
    {
      PT_INTERNAL_ERROR (parser, "merge update");
      parser_free_tree (parser, copy_assigns);
      goto cleanup;
    }

  /* save assignment list and replace within statement with the copy */
  save_assigns = info->update.assignment;
  info->update.assignment = copy_assigns;

  aptr_statement = pt_to_merge_update_query (parser, select_values, info);

  /* restore assignment list and destroy the copy */
  info->update.assignment = save_assigns;
  parser_free_tree (parser, copy_assigns);

  /* restore tree structure; pt_get_assignment_lists() */
  pt_restore_assignment_links (info->update.assignment, links, -1);

  if (aptr_statement == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR && !pt_has_error (parser))
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  aptr_statement = mq_translate (parser, aptr_statement);
  if (aptr_statement == NULL)
    {
      if (pt_has_error (parser))
    {
      pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, aptr_statement);
    }
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  xasl = pt_make_aptr_parent_node (parser, aptr_statement, UPDATE_PROC);
  if (xasl == NULL || xasl->aptr_list == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR && !pt_has_error (parser))
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  /* for subquery cache */
  if (aptr_statement->xasl_id && !statement->flag.do_not_use_subquery_cache)
    {
      xasl->sub_xasl_id = aptr_statement->xasl_id;
      xasl->sub_host_var_count = aptr_statement->sub_host_var_count;
      xasl->sub_host_var_index = aptr_statement->sub_host_var_index;
    }

  /* flush all classes */
  p = from;
  while (p != NULL)
    {
      cl_name_node = p->info.spec.flat_entity_list;
      while (cl_name_node != NULL)
    {
      error = locator_flush_class (cl_name_node->info.name.db_object);
      if (error != NO_ERROR)
        {
          goto cleanup;
        }
      cl_name_node = cl_name_node->next;
    }
      p = p->next;
    }

  update = &xasl->proc.update;

  update->num_classes = 1;
  update->num_assigns = num_vals;

  regu_array_alloc (&update->classes, 1);
  if (update->classes == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  regu_array_alloc (&update->assigns, update->num_assigns);
  if (update->assigns == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  upd_cls = &update->classes[0];

  upd_cls->has_uniques = (from->info.spec.flag & PT_SPEC_FLAG_HAS_UNIQUE);

  /* count subclasses of update class */
  num_subclasses = 0;
  cl_name_node = from->info.spec.flat_entity_list;
  while (cl_name_node)
    {
      num_subclasses++;
      cl_name_node = cl_name_node->next;
    }
  upd_cls->num_subclasses = num_subclasses;

  /* count class assignments */
  a = 0;
  pt_init_assignments_helper (parser, &assign_helper, assigns);
  while (pt_get_next_assignment (&assign_helper) != NULL)
    {
      if (assign_helper.lhs->info.name.spec_id == from->info.spec.id)
    {
      a++;
    }
    }
  upd_cls->num_attrs = a;

  /* allocate array for subclasses OIDs, hfids, attributes ids */
  upd_cls->class_oid = regu_oid_array_alloc (num_subclasses);
  if (upd_cls->class_oid == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  regu_array_alloc (&upd_cls->class_hfid, num_subclasses);
  if (upd_cls->class_hfid == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  upd_cls->att_id = regu_int_array_alloc (num_subclasses * upd_cls->num_attrs);
  if (upd_cls->att_id == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  cl_name_node = from->info.spec.flat_entity_list;
  class_obj = cl_name_node->info.name.db_object;
  error = sm_partitioned_class_type (class_obj, &upd_cls->needs_pruning, NULL, NULL);
  if (error != NO_ERROR)
    {
      goto cleanup;
    }

  /* iterate through subclasses */
  cl = 0;
  cl_name_node = from->info.spec.flat_entity_list;
  while (cl_name_node && error == NO_ERROR)
    {
      class_obj = cl_name_node->info.name.db_object;

      /* get class oid */
      class_oid = ws_identifier (class_obj);
      if (class_oid == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_HEAP_UNKNOWN_OBJECT, 3, 0, 0, 0);
      error = ER_HEAP_UNKNOWN_OBJECT;
      goto cleanup;
    }

      /* get hfid */
      hfid = sm_get_ch_heap (class_obj);
      if (hfid == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
        {
          error = ER_GENERIC_ERROR;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
        }
      goto cleanup;
    }

      upd_cls->class_oid[cl] = *class_oid;
      upd_cls->class_hfid[cl] = *hfid;

      /* Calculate attribute ids and link each assignment to classes and attributes */
      pt_init_assignments_helper (parser, &assign_helper, assigns);
      assign_idx = a = 0;
      while ((att_name_node = pt_get_next_assignment (&assign_helper)) != NULL)
    {
      if (att_name_node->info.name.spec_id == cl_name_node->info.name.spec_id)
        {
          assign = &update->assigns[assign_idx];
          assign->cls_idx = 0;
          assign->att_idx = a;
          upd_cls->att_id[cl * upd_cls->num_attrs + a] = sm_att_id (class_obj, att_name_node->info.name.original);

          if (upd_cls->att_id[cl * upd_cls->num_attrs + a] < 0)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
          if (error == NO_ERROR && !pt_has_error (parser))
            {
              error = ER_GENERIC_ERROR;
              er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
            }
          goto cleanup;
        }
          /* count attributes for current class */
          a++;
        }
      /* count assignments */
      assign_idx++;
    }

      /* count subclasses */
      cl++;
      cl_name_node = cl_name_node->next;
    }

  update->wait_msecs = XASL_WAIT_MSECS_NOCHANGE;
  hint_arg = info->waitsecs_hint;
  if (info->hint & PT_HINT_LK_TIMEOUT && PT_IS_HINT_NODE (hint_arg))
    {
      float hint_wait_secs = (float) atof (hint_arg->info.name.original);
      if (hint_wait_secs > 0)
    {
      update->wait_msecs = (int) (hint_wait_secs * 1000);
    }
      else
    {
      update->wait_msecs = (int) hint_wait_secs;
    }
    }
  update->no_logging = (info->hint & PT_HINT_NO_LOGGING);

  /* check constants */
  class_obj = from->info.spec.flat_entity_list->info.name.db_object;

  pt_init_assignments_helper (parser, &assign_helper, assigns);
  a = 0;
  while ((att_name_node = pt_get_next_assignment (&assign_helper)) != NULL)
    {
      PT_NODE *node, *prev, *next;
      /* process only constants assigned to current class attributes */
      if (att_name_node->info.name.spec_id != from->info.spec.id || !assign_helper.is_rhs_const)
    {
      /* this is a constant assignment */
      a++;
      continue;
    }
      /* get DB_VALUE of assignment's right argument */
      val = pt_value_to_db (parser, assign_helper.assignment->info.expr.arg2);
      if (val == NULL)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_GENERIC_ERROR, 0);
      goto cleanup;
    }

      prev = NULL;
      for (node = *non_null_attrs; node != NULL; node = next)
    {
      /* Check to see if this is a NON NULL attr */
      next = node->next;

      if (!pt_name_equal (parser, node, att_name_node))
        {
          prev = node;
          continue;
        }

      if (DB_IS_NULL (val))
        {
          /* assignment of a NULL value to a non null attribute */
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_ATTRIBUTE_CANT_BE_NULL, 1,
              att_name_node->info.name.original);
          error = ER_OBJ_ATTRIBUTE_CANT_BE_NULL;
          goto cleanup;
        }
      /* remove the node from the non_null_attrs list since we've already checked that the attr will be non-null
       * and the engine need not check again. */
      if (prev == NULL)
        {
          *non_null_attrs = (*non_null_attrs)->next;
        }
      else
        {
          prev->next = node->next;
        }

      /* free the node */
      node->next = NULL;    /* cut-off link */
      parser_free_tree (parser, node);
      break;
    }

      /* Coerce constant value to destination attribute type */
      regu_alloc (update->assigns[a].constant);
      if (update->assigns[a].constant == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
        {
          error = ER_GENERIC_ERROR;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
        }
      goto cleanup;
    }
      attr = db_get_attribute (class_obj, att_name_node->info.name.original);
      if (attr == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
        {
          error = ER_GENERIC_ERROR;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
        }
      goto cleanup;
    }
      dom = db_attribute_domain (attr);
      error = tp_value_coerce (val, update->assigns[a].constant, dom);
      if (error != DOMAIN_COMPATIBLE)
    {
      error = ER_OBJ_DOMAIN_CONFLICT;
      er_set (ER_ERROR_SEVERITY, __FILE__, __LINE__, error, 1, att_name_node->info.name.original);
      goto cleanup;
    }

      /* count assignments */
      a++;
    }

  /* generate xasl for non-null constraints predicates */
  error =
    pt_get_assignment_lists (parser, &select_names, &select_values, &const_names, &const_values, &num_vals, &num_consts,
                 info->update.assignment, &links);
  if (error != NO_ERROR)
    {
      PT_INTERNAL_ERROR (parser, "merge update");
      goto cleanup;
    }

  /* need to jump upd_del_class_cnt OID-CLASS OID pairs */
  attr_offset = ((aptr_statement->info.query.upd_del_class_cnt + aptr_statement->info.query.mvcc_reev_extra_cls_cnt) * 2
         + (info->update.has_delete ? 1 : 0));

  error = pt_to_constraint_pred (parser, xasl, info->into, *non_null_attrs, select_names, attr_offset);
#if 0
/* disabled temporary in MVCC */
  attr_offset = aptr_statement->info.query.upd_del_class_cnt * 2 + (info->update.has_delete ? 1 : 0);
  error = pt_to_constraint_pred (parser, xasl, info->into, *non_null_attrs, select_names, attr_offset);
#endif

  pt_restore_assignment_links (info->update.assignment, links, -1);
  if (error != NO_ERROR)
    {
      goto cleanup;
    }

  aptr = xasl->aptr_list;

  xasl->n_oid_list = aptr->n_oid_list;
  aptr->n_oid_list = 0;
  xasl->class_oid_list = aptr->class_oid_list;
  aptr->class_oid_list = NULL;
  xasl->class_locks = aptr->class_locks;
  aptr->class_locks = NULL;
  xasl->tcard_list = aptr->tcard_list;
  aptr->tcard_list = NULL;
  xasl->dbval_cnt = aptr->dbval_cnt;

  /* set TDE flag */
  XASL_SET_FLAG (xasl, aptr->flag & XASL_INCLUDES_TDE_CLASS);
  XASL_CLEAR_FLAG (aptr, XASL_INCLUDES_TDE_CLASS);

  /* fill in XASL cache related information */
  /* OID of the user who is creating this XASL */
  if ((oid = ws_identifier (db_get_user ())) != NULL)
    {
      COPY_OID (&xasl->creator_oid, oid);
    }
  else
    {
      OID_SET_NULL (&xasl->creator_oid);
    }

cleanup:
  if (aptr_statement != NULL)
    {
      parser_free_tree (parser, aptr_statement);
    }
  if (from != NULL)
    {
      parser_free_tree (parser, from);
    }
  if (error != NO_ERROR)
    {
      xasl = NULL;
    }

  return xasl;
}

/*
 * pt_to_merge_insert_xasl () - Generate XASL for INSERT part of MERGE
 *   return:
 *   parser(in):
 *   statement(in):
 *   non_null_attrs(in):
 *   default_expr_attrs(in):
 */
static XASL_NODE *
pt_to_merge_insert_xasl (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * non_null_attrs,
             PT_NODE * default_expr_attrs)
{
  XASL_NODE *xasl = NULL, *aptr;
  INSERT_PROC_NODE *insert = NULL;
  PT_NODE *aptr_statement = NULL;
  PT_NODE *values_list;
  PT_NODE *attr, *attrs;
  MOBJ class_;
  OID *class_oid, *oid;
  DB_OBJECT *class_obj;
  SM_CLASS *smclass = NULL;
  HFID *hfid;
  int num_vals, num_default_expr, a;
  int error = NO_ERROR;
  PT_NODE *hint_arg;

  values_list = statement->info.merge.insert.value_clauses;
  if (values_list == NULL)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      return NULL;
    }

  aptr_statement = pt_to_merge_insert_query (parser, values_list->info.node_list.list, &statement->info.merge);
  if (aptr_statement == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR && !pt_has_error (parser))
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      return NULL;
    }
  aptr_statement = mq_translate (parser, aptr_statement);
  if (aptr_statement == NULL)
    {
      if (pt_has_error (parser))
    {
      pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, aptr_statement);
    }
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      return NULL;
    }

  attrs = statement->info.merge.insert.attr_list;
  class_obj = statement->info.merge.into->info.spec.flat_entity_list->info.name.db_object;

  class_ = locator_create_heap_if_needed (class_obj, sm_is_reuse_oid_class (class_obj));
  if (class_ == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  hfid = sm_ch_heap (class_);
  if (hfid == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  if (locator_flush_class (class_obj) != NO_ERROR)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  num_default_expr = pt_length_of_list (default_expr_attrs);
  num_vals = pt_length_of_select_list (pt_get_select_list (parser, aptr_statement), EXCLUDE_HIDDEN_COLUMNS);

  xasl = pt_make_aptr_parent_node (parser, aptr_statement, INSERT_PROC);
  if (xasl == NULL || xasl->aptr_list == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR && !pt_has_error (parser))
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  /* for subquery cache */
  if (aptr_statement->xasl_id && !statement->flag.do_not_use_subquery_cache)
    {
      xasl->sub_xasl_id = aptr_statement->xasl_id;
      xasl->sub_host_var_count = aptr_statement->sub_host_var_count;
      xasl->sub_host_var_index = aptr_statement->sub_host_var_index;
    }

  insert = &xasl->proc.insert;
  insert->class_hfid = *hfid;
  class_oid = ws_identifier (class_obj);
  if (class_oid)
    {
      insert->class_oid = *class_oid;
    }
  else
    {
      error = ER_HEAP_UNKNOWN_OBJECT;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      goto cleanup;
    }

  insert->has_uniques = (statement->info.merge.flags & PT_MERGE_INFO_HAS_UNIQUE);
  insert->wait_msecs = XASL_WAIT_MSECS_NOCHANGE;
  hint_arg = statement->info.merge.waitsecs_hint;
  if (statement->info.merge.hint & PT_HINT_LK_TIMEOUT && PT_IS_HINT_NODE (hint_arg))
    {
      float hint_wait_secs = (float) atof (hint_arg->info.name.original);
      if (hint_wait_secs > 0)
    {
      insert->wait_msecs = (int) (hint_wait_secs * 1000);
    }
      else
    {
      insert->wait_msecs = (int) hint_wait_secs;
    }
    }
  insert->no_logging = (statement->info.merge.hint & PT_HINT_NO_LOGGING);
  insert->do_replace = 0;

  if (num_vals + num_default_expr > 0)
    {
      insert->att_id = regu_int_array_alloc (num_vals + num_default_expr);
      if (insert->att_id)
    {
      /* the identifiers of the attributes that have a default expression are placed first */
      for (attr = default_expr_attrs, a = 0; error >= 0 && a < num_default_expr; attr = attr->next, ++a)
        {
          if ((insert->att_id[a] = sm_att_id (class_obj, attr->info.name.original)) < 0)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
        }
        }
      for (attr = attrs, a = num_default_expr; error >= 0 && a < num_default_expr + num_vals;
           attr = attr->next, ++a)
        {
          if ((insert->att_id[a] = sm_att_id (class_obj, attr->info.name.original)) < 0)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
        }
        }
      insert->vals = NULL;
      insert->num_vals = num_vals + num_default_expr;
      insert->num_default_expr = num_default_expr;
    }
      else
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
    }
      if (error != NO_ERROR)
    {
      goto cleanup;
    }
    }

  error = sm_partitioned_class_type (class_obj, &insert->pruning_type, NULL, NULL);
  if (error != NO_ERROR)
    {
      goto cleanup;
    }

  error = pt_to_constraint_pred (parser, xasl, statement->info.merge.into, non_null_attrs, attrs, 0);
  if (error != NO_ERROR)
    {
      goto cleanup;
    }

  /* fill in XASL cache related information */
  aptr = xasl->aptr_list;

  /* OID of the user who is creating this XASL */
  oid = ws_identifier (db_get_user ());
  if (oid != NULL)
    {
      COPY_OID (&xasl->creator_oid, oid);
    }
  else
    {
      OID_SET_NULL (&xasl->creator_oid);
    }

  assert (locator_is_class (class_obj, DB_FETCH_QUERY_READ) > 0);
  (void) au_fetch_class (class_obj, &smclass, AU_FETCH_READ, AU_SELECT);

  /* list of class OIDs used in this XASL */
  /* reserve spec oid space by 1+ */
  xasl->class_oid_list = regu_oid_array_alloc (1 + aptr->n_oid_list);
  xasl->class_locks = regu_int_array_alloc (1 + aptr->n_oid_list);
  xasl->tcard_list = regu_int_array_alloc (1 + aptr->n_oid_list);
  if (xasl->class_oid_list == NULL || xasl->class_locks == NULL || xasl->tcard_list == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      goto cleanup;
    }

  xasl->n_oid_list = 1 + aptr->n_oid_list;

  /* copy aptr oids to xasl */
  (void) memcpy (xasl->class_oid_list + 1, aptr->class_oid_list, sizeof (OID) * aptr->n_oid_list);
  (void) memcpy (xasl->class_locks + 1, aptr->class_locks, sizeof (int) * aptr->n_oid_list);
  (void) memcpy (xasl->tcard_list + 1, aptr->tcard_list, sizeof (int) * aptr->n_oid_list);

  /* set spec oid */
  xasl->class_oid_list[0] = insert->class_oid;
  xasl->class_locks[0] = (int) IX_LOCK;
  xasl->tcard_list[0] = XASL_CLASS_NO_TCARD;    /* init #pages */
  xasl->dbval_cnt = aptr->dbval_cnt;

  /* set TDE flag */
  XASL_SET_FLAG (xasl, aptr->flag & XASL_INCLUDES_TDE_CLASS);

  if (smclass)
    {
      if (smclass->tde_algorithm != TDE_ALGORITHM_NONE)
    {
      XASL_SET_FLAG (xasl, XASL_INCLUDES_TDE_CLASS);
    }
    }

cleanup:
  if (aptr_statement != NULL)
    {
      parser_free_tree (parser, aptr_statement);
    }
  if (error != NO_ERROR)
    {
      xasl = NULL;
    }

  return xasl;
}

/*
 * pt_substitute_assigned_name_node () - substitute name node with assigned
 *                   expression
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in/out):
 *   continue_walk(in):
 */
PT_NODE *
pt_substitute_assigned_name_node (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE *assignments = (PT_NODE *) arg;
  PT_ASSIGNMENTS_HELPER ea;

  if (node->node_type == PT_NAME)
    {
      pt_init_assignments_helper (parser, &ea, assignments);
      while (pt_get_next_assignment (&ea) != NULL)
    {
      if (pt_name_equal (parser, ea.lhs, node))
        {
          parser_free_tree (parser, node);
          node = parser_copy_tree_list (parser, ea.rhs);
          break;
        }
    }
    }

  return node;
}

/*
 * pt_set_orderby_for_sort_limit_plan () - setup ORDER BY list to be applied
 *                     to a SORT-LIMIT plan
 * return : ORDER BY list on success, NULL on error
 * parser (in) : parser context
 * statement (in) : statement
 * nodes_list (in/out) : list of nodes referenced by the plan
 *
 * Note: if an ORDER BY spec is not a name, the node it references is added
 * to the nodes_list nodes.
 */
PT_NODE *
pt_set_orderby_for_sort_limit_plan (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * nodes_list)
{
  PT_NODE *order_by = NULL, *select_list = NULL, *new_order_by = NULL;
  PT_NODE *sort_spec = NULL, *node = NULL, *name = NULL, *sort = NULL;
  PT_NODE *prev = NULL;
  int pos = 0, names_count = 0, added_count = 0;
  bool add_node = false;

  order_by = statement->info.query.order_by;
  select_list = pt_get_select_list (parser, statement);

  /* count nodes in name_list, we will add new nodes at the end of the list */
  node = nodes_list;
  names_count = 0;
  while (node)
    {
      prev = node;
      names_count++;
      node = node->next;
    }

  /* create a new ORDER BY list which reflects positions of nodes in the nodes_list */
  for (sort_spec = order_by; sort_spec != NULL; sort_spec = sort_spec->next)
    {
      add_node = true;

      if (sort_spec->node_type != PT_SORT_SPEC)
    {
      assert_release (sort_spec->node_type == PT_SORT_SPEC);
      goto error_return;
    }
      /* find the node which is referenced by this sort_spec */
      for (pos = 1, node = select_list; node != NULL; pos++, node = node->next)
    {
      if (pos == sort_spec->info.sort_spec.pos_descr.pos_no)
        {
          break;
        }
    }

      if (node == NULL)
    {
      assert_release (node != NULL);
      goto error_return;
    }

      CAST_POINTER_TO_NODE (node);

      if (node->node_type == PT_NAME)
    {
      /* SORT-LIMIT plans are build over the subset of classes referenced in the ORDER BY clause. This means that
       * any name referenced in ORDER BY must also be a segment for one of the classes of this subplan. We just
       * need to update the sort_spec position. */
      for (pos = 1, name = nodes_list; name != NULL; pos++, name = name->next)
        {
          if (pt_name_equal (parser, name, node))
        {
          break;
        }
        }

      if (name != NULL)
        {
          add_node = false;
        }
    }

      if (add_node)
    {
      /* this node was not found in the node_list. In order to be able to execute the ORDER BY clause, we have to
       * add it to the list */
      pos = names_count + added_count + 1;
      name = pt_point (parser, node);
      if (name == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          goto error_return;
        }
      /* add node to the end of name_list */
      prev->next = name;
      prev = prev->next;
      added_count++;
    }
      else
    {
      /* just point to it */
      name = pt_point (parser, name);
      if (name == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          goto error_return;
        }
    }

      /* create a new sort_spec for the original one and add it to the list */
      sort = parser_new_node (parser, PT_SORT_SPEC);
      if (sort == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      goto error_return;
    }

      sort->info.sort_spec.expr = name;
      sort->info.sort_spec.pos_descr.pos_no = pos;
      sort->info.sort_spec.asc_or_desc = sort_spec->info.sort_spec.asc_or_desc;

      CAST_POINTER_TO_NODE (name);
      sort->info.sort_spec.pos_descr.dom = pt_xasl_node_to_domain (parser, name);

      new_order_by = parser_append_node (sort, new_order_by);
    }

  return new_order_by;

error_return:
  if (new_order_by != NULL)
    {
      parser_free_tree (parser, new_order_by);
    }
  return NULL;
}

/*
 * pt_is_sort_list_covered () - same as qfile_is_sort_list_covered ()
 *   return: true or false
 *   parser (in) : parser context
 *   covering_list(in): covering sort item list pointer
 *   covered_list(in): covered sort item list pointer
 *
 * Note: if covering_list covers covered_list returns true.
 *       otherwise, returns false.
 */
static bool
pt_is_sort_list_covered (PARSER_CONTEXT * parser, SORT_LIST * covering_list_p, SORT_LIST * covered_list_p)
{
  SORT_LIST *s1, *s2;

  if (covered_list_p == NULL)
    {
      return false;
    }

  for (s1 = covering_list_p, s2 = covered_list_p; s1 && s2; s1 = s1->next, s2 = s2->next)
    {
      if (s1->s_order != s2->s_order || s1->s_nulls != s2->s_nulls || s1->pos_descr.pos_no != s2->pos_descr.pos_no)
    {
      return false;
    }
    }

  if (s1 == NULL && s2)
    {
      return false;
    }
  else
    {
      return true;
    }
}

/*
 * pt_reserved_id_to_valuelist_index () - Generate the index of value for
 *                    reserved attribute in the array
 *                    of cached attribute values.
 *
 * return       : Index of value.
 * parser (in)      : Parser context.
 * reserved_id (in) : Reserved name id.
 */
static int
pt_reserved_id_to_valuelist_index (PARSER_CONTEXT * parser, PT_RESERVED_NAME_ID reserved_id)
{
  switch (reserved_id)
    {
      /* Record info names */
    case RESERVED_T_PAGEID:
      return HEAP_RECORD_INFO_T_PAGEID;
    case RESERVED_T_SLOTID:
      return HEAP_RECORD_INFO_T_SLOTID;
    case RESERVED_T_VOLUMEID:
      return HEAP_RECORD_INFO_T_VOLUMEID;
    case RESERVED_T_OFFSET:
      return HEAP_RECORD_INFO_T_OFFSET;
    case RESERVED_T_LENGTH:
      return HEAP_RECORD_INFO_T_LENGTH;
    case RESERVED_T_REC_TYPE:
      return HEAP_RECORD_INFO_T_REC_TYPE;
    case RESERVED_T_REPRID:
      return HEAP_RECORD_INFO_T_REPRID;
    case RESERVED_T_CHN:
      return HEAP_RECORD_INFO_T_CHN;
    case RESERVED_T_MVCC_INSID:
      return HEAP_RECORD_INFO_T_MVCC_INSID;
    case RESERVED_T_MVCC_DELID:
      return HEAP_RECORD_INFO_T_MVCC_DELID;
    case RESERVED_T_MVCC_FLAGS:
      return HEAP_RECORD_INFO_T_MVCC_FLAGS;
    case RESERVED_T_MVCC_PREV_VERSION_LSA:
      return HEAP_RECORD_INFO_T_MVCC_PREV_VERSION;

      /* Page info names */
    case RESERVED_P_CLASS_OID:
      return HEAP_PAGE_INFO_CLASS_OID;
    case RESERVED_P_CUR_VOLUMEID:
      return HEAP_PAGE_INFO_CUR_VOLUME;
    case RESERVED_P_CUR_PAGEID:
      return HEAP_PAGE_INFO_CUR_PAGE;
    case RESERVED_P_PREV_PAGEID:
      return HEAP_PAGE_INFO_PREV_PAGE;
    case RESERVED_P_NEXT_PAGEID:
      return HEAP_PAGE_INFO_NEXT_PAGE;
    case RESERVED_P_NUM_SLOTS:
      return HEAP_PAGE_INFO_NUM_SLOTS;
    case RESERVED_P_NUM_RECORDS:
      return HEAP_PAGE_INFO_NUM_RECORDS;
    case RESERVED_P_ANCHOR_TYPE:
      return HEAP_PAGE_INFO_ANCHOR_TYPE;
    case RESERVED_P_ALIGNMENT:
      return HEAP_PAGE_INFO_ALIGNMENT;
    case RESERVED_P_TOTAL_FREE:
      return HEAP_PAGE_INFO_TOTAL_FREE;
    case RESERVED_P_CONT_FREE:
      return HEAP_PAGE_INFO_CONT_FREE;
    case RESERVED_P_OFFSET_TO_FREE_AREA:
      return HEAP_PAGE_INFO_OFFSET_TO_FREE_AREA;
    case RESERVED_P_IS_SAVING:
      return HEAP_PAGE_INFO_IS_SAVING;
    case RESERVED_P_UPDATE_BEST:
      return HEAP_PAGE_INFO_UPDATE_BEST;

      /* Key info names */
    case RESERVED_KEY_VOLUMEID:
      return BTREE_KEY_INFO_VOLUMEID;
    case RESERVED_KEY_PAGEID:
      return BTREE_KEY_INFO_PAGEID;
    case RESERVED_KEY_SLOTID:
      return BTREE_KEY_INFO_SLOTID;
    case RESERVED_KEY_KEY:
      return BTREE_KEY_INFO_KEY;
    case RESERVED_KEY_OID_COUNT:
      return BTREE_KEY_INFO_OID_COUNT;
    case RESERVED_KEY_FIRST_OID:
      return BTREE_KEY_INFO_FIRST_OID;
    case RESERVED_KEY_OVERFLOW_KEY:
      return BTREE_KEY_INFO_OVERFLOW_KEY;
    case RESERVED_KEY_OVERFLOW_OIDS:
      return BTREE_KEY_INFO_OVERFLOW_OIDS;

      /* B-tree node info names */
    case RESERVED_BT_NODE_VOLUMEID:
      return BTREE_NODE_INFO_VOLUMEID;
    case RESERVED_BT_NODE_PAGEID:
      return BTREE_NODE_INFO_PAGEID;
    case RESERVED_BT_NODE_TYPE:
      return BTREE_NODE_INFO_NODE_TYPE;
    case RESERVED_BT_NODE_KEY_COUNT:
      return BTREE_NODE_INFO_KEY_COUNT;
    case RESERVED_BT_NODE_FIRST_KEY:
      return BTREE_NODE_INFO_FIRST_KEY;
    case RESERVED_BT_NODE_LAST_KEY:
      return BTREE_NODE_INFO_LAST_KEY;

    default:
      /* unknown reserved id or not handled */
      assert (0);
      return RESERVED_NAME_INVALID;
    }
}

/*
 * pt_to_cume_dist_percent_rank_regu_variable () - generate regu_variable
 *                   for 'CUME_DIST' and 'PERCENT_RANK'
 *   return: REGU_VARIABLE*
 *   parser(in):
 *   tree(in):
 *   unbox(in):
 */
static REGU_VARIABLE *
pt_to_cume_dist_percent_rank_regu_variable (PARSER_CONTEXT * parser, PT_NODE * tree, UNBOX unbox)
{
  REGU_VARIABLE *regu = NULL;
  PT_NODE *arg_list = NULL, *orderby_list = NULL, *node = NULL;
  REGU_VARIABLE_LIST regu_var_list, regu_var;

  /* set up regu var */
  regu_alloc (regu);
  if (regu == NULL)
    {
      return NULL;
    }

  regu->type = TYPE_REGU_VAR_LIST;
  regu->domain = tp_domain_resolve_default (DB_TYPE_VARIABLE);
  /* for cume_dist and percent_rank, regu_variable should be hidden */
  REGU_VARIABLE_SET_FLAG (regu, REGU_VARIABLE_HIDDEN_COLUMN);
  arg_list = tree->info.function.arg_list;
  orderby_list = tree->info.function.order_by;
  assert (arg_list != NULL && orderby_list != NULL);

  /* first insert the first order by item */
  regu_var_list = pt_to_regu_variable_list (parser, orderby_list->info.sort_spec.expr, UNBOX_AS_VALUE, NULL, NULL);
  if (regu_var_list == NULL)
    {
      return NULL;
    }

  /* insert order by items one by one */
  regu_var = regu_var_list;
  for (node = orderby_list->next; node != NULL; node = node->next)
    {
      regu_var->next = pt_to_regu_variable_list (parser, node->info.sort_spec.expr, UNBOX_AS_VALUE, NULL, NULL);
      regu_var = regu_var->next;
    }

  /* order by items have been attached, now the arguments */
  regu_var->next = pt_to_regu_variable_list (parser, arg_list, UNBOX_AS_VALUE, NULL, NULL);

  /* finally setup regu: */
  regu->value.regu_var_list = regu_var_list;

  return regu;
}

static int
pt_set_like_recompile_candidate (PARSER_CONTEXT * parser, QO_PLAN * qo_plan, XASL_NODE * xasl)
{
  if (qo_plan == NULL)
    {
      return NO_ERROR;
    }

  bool is_candidate = false;

  if (qo_has_like_recompile_candidate (qo_plan, &is_candidate) == NO_ERROR)
    {
      if (is_candidate)
    {
      xasl->header.xasl_flag |= LIKE_RECOMPILE_CANDIDATE;
    }
    }

  return NO_ERROR;
}

/*
 * pt_set_limit_optimization_flags () - setup XASL flags according to
 *                  query limit optimizations applied
 *                  during plan generation
 * return : error code or NO_ERROR
 * parser (in)  : parser context
 * qo_plan (in) : query plan
 * xasl (in)    : xasl node
 */
static int
pt_set_limit_optimization_flags (PARSER_CONTEXT * parser, QO_PLAN * qo_plan, XASL_NODE * xasl)
{
  if (qo_plan == NULL)
    {
      return NO_ERROR;
    }

  /* Set SORT-LIMIT flags */
  if (qo_has_sort_limit_subplan (qo_plan))
    {
      xasl->header.xasl_flag |= SORT_LIMIT_USED;
      xasl->header.xasl_flag |= SORT_LIMIT_CANDIDATE;
    }
  else
    {
      switch (qo_plan->info->env->use_sort_limit)
    {
    case QO_SL_USE:
      /* A SORT-LIMIT plan can be created but planner found a better plan. In this case, there is no point in
       * recompiling the plan a second time. There are cases in which suppling a smaller limit to the query will
       * cause planner to choose a SORT-LIMIT plan over the current one but, since there is no way to know if this
       * is the case, it is better to consider that this query will never use SORT-LIMIT. */
      break;

    case QO_SL_INVALID:
      /* A SORT-LIMIT plan cannot be generated for this query */
      break;

    case QO_SL_POSSIBLE:
      /* The query might produce a SORT-LIMIT plan but the supplied limit. could not be evaluated. */
      xasl->header.xasl_flag |= SORT_LIMIT_CANDIDATE;
      break;
    }
    }

  /* Set MULTI-RANGE-OPTIMIZATION flags */
  if (qo_plan_multi_range_opt (qo_plan))
    {
      /* qo_plan->multi_range_opt_use == PLAN_MULTI_RANGE_OPT_USE */
      /* convert ordbynum to key limit if we have iscan with multiple key ranges */
      int err = pt_ordbynum_to_key_limit_multiple_ranges (parser, qo_plan,
                              xasl);
      if (err != NO_ERROR)
    {
      return err;
    }

      xasl->header.xasl_flag |= MRO_CANDIDATE;
      xasl->header.xasl_flag |= MRO_IS_USED;
    }
  else if (qo_plan->multi_range_opt_use == PLAN_MULTI_RANGE_OPT_CAN_USE)
    {
      /* Query could use multi range optimization, but limit was too large */
      xasl->header.xasl_flag |= MRO_CANDIDATE;
    }

  return NO_ERROR;
}

/*
 * pt_aggregate_info_append_value_list () - Appends the value_list in the aggregate info->value_list, increasing also
 *                                          the val_cnt
 * info        (in/out)  :
 * value_list  (in)      :
 */
static void
pt_aggregate_info_append_value_list (AGGREGATE_INFO * info, VAL_LIST * value_list)
{
  assert (info != NULL && info->value_list != NULL && value_list != NULL);

  // increase the size with the number of elements in the value_list
  info->value_list->val_cnt += value_list->val_cnt;

  QPROC_DB_VALUE_LIST value_temp = NULL;

  // get the end of the list
  for (value_temp = info->value_list->valp; value_temp->next != NULL; value_temp = value_temp->next)
    ;

  assert (value_temp != NULL);

  // append to the end
  value_temp->next = value_list->valp;
}

/*
 * pt_aggregate_info_update_value_and_reguvar_lists () - Merges the arguments in the aggregate info corresponding lists
 * info                (in/out)  :
 * value_list          (in)      :
 * regu_position_list  (in)      :
 * regu_constant_list  (in)      :
 */
static void
pt_aggregate_info_update_value_and_reguvar_lists (AGGREGATE_INFO * info, VAL_LIST * value_list,
                          REGU_VARIABLE_LIST regu_position_list,
                          REGU_VARIABLE_LIST regu_constant_list)
{
  pt_aggregate_info_append_value_list (info, value_list);

  pt_merge_regu_var_lists (&info->regu_list, regu_position_list);

  // also increment list count
  int regu_constant_list_size = 0;

  for (REGU_VARIABLE_LIST ptr = regu_constant_list; ptr != NULL; ptr = ptr->next, regu_constant_list_size++)
    {
      ptr->value.vfetch_to = pt_index_value (value_list, regu_constant_list_size);
    }

  pt_merge_regu_var_lists (&info->out_list->valptrp, regu_constant_list);
  info->out_list->valptr_cnt += regu_constant_list_size;
}

/*
 * pt_aggregate_info_update_scan_regu_list () - Merges scan_regu_list in the aggregate info->scan_regu_list
 * info                (in/out)  :
 * scan_regu_list      (in)      :
 */
static void
pt_aggregate_info_update_scan_regu_list (AGGREGATE_INFO * info, REGU_VARIABLE_LIST scan_regu_list)
{
  REGU_VARIABLE_LIST tail = NULL;
  int scan_regu_list_size = 0;
  int index = 0;

  // calculate the size of scan_regu_var_list
  for (tail = scan_regu_list; tail != NULL; tail = tail->next, scan_regu_list_size++)
    ;

  // start fetching for the last scan_regu_var_list_size elements
  index = info->value_list->val_cnt - scan_regu_list_size;

  for (REGU_VARIABLE_LIST itr = scan_regu_list; itr != NULL; itr = itr->next)
    {
      // get the value from the value_list
      itr->value.vfetch_to = pt_index_value (info->value_list, index++);
    }

  // append scan_regu_list to info
  pt_merge_regu_var_lists (&info->scan_regu_list, scan_regu_list);
}

/*
 * pt_node_list_to_value_and_reguvar_list () - Constructs the value_list and regu_position_list from node
 * parser               (in)      :
 * node                 (in)      :
 * value_list           (in/out)  :
 * regu_position_list   (in/out)  :
 */
static PT_NODE *
pt_node_list_to_value_and_reguvar_list (PARSER_CONTEXT * parser, PT_NODE * node, VAL_LIST ** value_list,
                    REGU_VARIABLE_LIST * regu_position_list)
{
  assert (node != NULL && value_list != NULL);

  *value_list = pt_make_val_list (parser, node);

  if (*value_list == NULL)
    {
      return NULL;
    }

  if (pt_make_regu_list_from_value_list (parser, node, *value_list, regu_position_list) == NULL)
    {
      return NULL;
    }

  return node;
}

/*
 * pt_make_regu_list_from_value_list () - creates a regu_list from value_list with TYPE POSITION
 * parser (in)         :
 * node (in)           :
 * value_list (in)     :
 * regu_list (in/out)  :
 */
static PT_NODE *
pt_make_regu_list_from_value_list (PARSER_CONTEXT * parser, PT_NODE * node, VAL_LIST * value_list,
                   REGU_VARIABLE_LIST * regu_list)
{
  assert (node != NULL && value_list != NULL && regu_list != NULL);

  int *attr_offsets = NULL;
  bool out_of_memory = false;

  attr_offsets = pt_make_identity_offsets (node);
  if (attr_offsets == NULL)
    {
      out_of_memory = true;
      goto end;
    }

  *regu_list = pt_to_position_regu_variable_list (parser, node, value_list, attr_offsets);
  if (*regu_list == NULL)
    {
      out_of_memory = true;
      goto end;
    }

end:
  if (attr_offsets != NULL)
    {
      free_and_init (attr_offsets);
    }

  if (out_of_memory)
    {
      /* on error, return NULL The error should be reported by the caller */
      node = NULL;
    }

  return node;
}

/*
 * pt_make_constant_regu_list_from_val_list () - creates a regu list with constant type from value_list
 * parser (in)         :
 * value_list (in)     :
 * regu_list (in/out)  :
 */
static int
pt_make_constant_regu_list_from_val_list (PARSER_CONTEXT * parser, VAL_LIST * value_list,
                      REGU_VARIABLE_LIST * regu_list)
{
  assert (*regu_list == NULL);

  size_t value_list_size = value_list->val_cnt;
  QPROC_DB_VALUE_LIST crt_val = value_list->valp;
  REGU_VARIABLE_LIST last = NULL;

  for (size_t i = 0; i < value_list_size; i++, crt_val = crt_val->next)
    {
      REGU_VARIABLE_LIST crt_regu;
      regu_alloc (crt_regu);
      if (crt_regu == NULL)
    {
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }

      crt_regu->value.type = TYPE_CONSTANT;
      crt_regu->value.domain = crt_val->dom;
      crt_regu->value.value.dbvalptr = crt_val->val;

      // set head
      if (*regu_list == NULL)
    {
      *regu_list = crt_regu;
      last = *regu_list;
    }
      // append
      else
    {
      last->next = crt_regu;
      last = last->next;
    }
    }

  return NO_ERROR;
}

static void
pt_set_regu_list_pos_descr_from_idx (REGU_VARIABLE_LIST & regu_list, size_t starting_index)
{
  for (REGU_VARIABLE_LIST crt_regu = regu_list; crt_regu != NULL; crt_regu = crt_regu->next)
    {
      assert (crt_regu->value.type == TYPE_POSITION);
      crt_regu->value.value.pos_descr.pos_no = (int) starting_index++;
    }
}

/*
 * pt_fix_interpolation_aggregate_function_order_by () -
 *
 * return :
 * parser (in)  :
 * node (in) :
 */
static PT_NODE *
pt_fix_interpolation_aggregate_function_order_by (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_FUNCTION_INFO *func_info_p = NULL;
  PT_NODE *sort_spec = NULL;

  assert (parser != NULL && node != NULL && node->node_type == PT_FUNCTION);

  func_info_p = &node->info.function;
  assert (!func_info_p->analytic.is_analytic);

  if (func_info_p->function_type == PT_GROUP_CONCAT || func_info_p->function_type == PT_CUME_DIST
      || func_info_p->function_type == PT_PERCENT_RANK)
    {
      /* nothing to be done for these cases */
      return node;
    }
  else if ((func_info_p->function_type == PT_PERCENTILE_CONT || func_info_p->function_type == PT_PERCENTILE_DISC)
       && func_info_p->order_by != NULL && func_info_p->order_by->info.sort_spec.pos_descr.pos_no == 0)
    {
      func_info_p->order_by->info.sort_spec.pos_descr.pos_no = 1;
      func_info_p->order_by->info.sort_spec.pos_descr.dom = pt_xasl_node_to_domain (parser, func_info_p->arg_list);
    }
  else if (func_info_p->function_type == PT_MEDIAN && func_info_p->arg_list != NULL
       && !PT_IS_CONST (func_info_p->arg_list) && func_info_p->order_by == NULL)
    {
      /* generate the sort spec for median */
      sort_spec = parser_new_node (parser, PT_SORT_SPEC);
      if (sort_spec == NULL)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      return NULL;
    }

      sort_spec->info.sort_spec.asc_or_desc = PT_ASC;
      sort_spec->info.sort_spec.nulls_first_or_last = PT_NULLS_DEFAULT;
      sort_spec->info.sort_spec.expr = parser_copy_tree (parser, node->info.function.arg_list);
      if (sort_spec->info.sort_spec.expr == NULL)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      return NULL;
    }

      sort_spec->info.sort_spec.pos_descr.pos_no = 1;
      sort_spec->info.sort_spec.pos_descr.dom = pt_xasl_node_to_domain (parser, func_info_p->arg_list);

      func_info_p->order_by = sort_spec;
    }

  return node;
}


/*
 * pt_fix_buildlist_aggregate_cume_dist_percent_rank () - This function generates
 *                       aggregate info for aggregate CUME_DIST/PERCENT_RANK in buildlist.
 *                       This is neccesary because for evaluation in buildlist, two functions
 *                       need re-scan order-by values therefore we must add function order-by elements
 *                       into regu_list, scan_regu_list, value_list and out_list.
 *
 * return : ERROR CODE
 * parser (in)  :
 * node (in) : order by node of CUME_DIST/PERCENT_RANK
 * info (in) :
 * regu (in) :
 */
static int
pt_fix_buildlist_aggregate_cume_dist_percent_rank (PARSER_CONTEXT * parser, PT_NODE * node, AGGREGATE_INFO * info,
                           REGU_VARIABLE * regu)
{
  REGU_VARIABLE_LIST regu_list, regu_var, regu_const, new_regu, tail, scan_regu_list, out_list;
  REGU_VARIABLE *scan_regu;
  QPROC_DB_VALUE_LIST value_tmp;
  VAL_LIST *value_list;
  TP_DOMAIN *domain;
  PT_NODE *pnode, *order, *pname;
  int i;

  assert (parser != NULL && node != NULL && info != NULL && regu != NULL && regu->type == TYPE_REGU_VAR_LIST);

  /* initialize variables */
  order = node;
  regu_list = regu->value.regu_var_list;
  regu_var = regu_const = regu_list;
  tail = info->regu_list;

  /* find length and tail of regu_list */
  if (tail == NULL)
    {
      i = 0;
    }
  else
    {
      for (tail = info->regu_list, i = 1; tail->next != NULL; i++, tail = tail->next);
    }

  /* for order by regu, we need to link the value pointer */
  while (regu_const != NULL)
    {
      /* create a new regu for function order by clause */
      regu_alloc (new_regu);
      if (new_regu == NULL)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      return ER_FAILED;
    }

      domain = pt_xasl_node_to_domain (parser, order->info.sort_spec.expr);
      if (domain == NULL)
    {
      return ER_FAILED;
    }

      /* regu list must be a position, and set the scan value to the regu_var which is used in the
       * CUME_DIST/PERCENT_RANK evaluation. */
      new_regu->value.type = TYPE_POSITION;
      new_regu->value.domain = domain;
      new_regu->value.value.pos_descr.pos_no = i++;
      new_regu->value.value.pos_descr.dom = domain;
      new_regu->value.vfetch_to = regu_var->value.value.dbvalptr;

      if (tail == NULL)
    {
      tail = new_regu;
    }
      else
    {
      tail->next = new_regu;
      tail = new_regu;
    }

      /* since the first half of regu_list are order by regu */
      order = order->next;
      regu_var = regu_var->next;
      regu_const = regu_const->next->next;
    }

  /* append scan_regu_list, out_list and value_list */
  scan_regu_list = info->scan_regu_list;
  out_list = info->out_list->valptrp;
  value_tmp = info->value_list->valp;

  for (pnode = node, regu_var = regu_list; pnode != NULL; pnode = pnode->next, regu_var = regu_var->next)
    {
      /* append scan_list */
      scan_regu = pt_to_regu_variable (parser, pnode->info.sort_spec.expr, UNBOX_AS_VALUE);
      if (scan_regu == NULL)
    {
      return ER_FAILED;
    }

      /* scan_regu->vfetch_to is also needed for domain checking */
      scan_regu->vfetch_to = regu_var->value.value.dbvalptr;

      if (scan_regu_list == NULL)
    {
      regu_alloc (scan_regu_list);
      if (scan_regu_list == NULL)
        {
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return ER_FAILED;
        }
    }
      else
    {
      while (scan_regu_list->next != NULL)
        {
          scan_regu_list = scan_regu_list->next;
        }
      regu_alloc (scan_regu_list->next);
      if (scan_regu_list->next == NULL)
        {
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return ER_FAILED;
        }

      scan_regu_list = scan_regu_list->next;
    }

      scan_regu_list->next = NULL;
      scan_regu_list->value = *scan_regu;

      /* appende out_list */
      pname = parser_new_node (parser, PT_VALUE);

      if (pname == NULL)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      return ER_FAILED;
    }

      pname->type_enum = PT_TYPE_INTEGER;
      pname->info.value.data_value.i = 0;
      parser_append_node (pname, info->out_names);

      if (out_list == NULL)
    {
      regu_alloc (out_list);
      if (out_list == NULL)
        {
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return ER_FAILED;
        }
    }
      else
    {
      while (out_list->next != NULL)
        {
          out_list = out_list->next;
        }

      regu_alloc (out_list->next);
      if (out_list->next == NULL)
        {
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return ER_FAILED;
        }

      out_list = out_list->next;
    }

      out_list->next = NULL;
      out_list->value = *scan_regu;
      info->out_list->valptr_cnt++;

      /* append value list, although this value in the value_list are useless for further evaluation, it is needed to
       * reserve the corresponding positions, otherwise out_list will be messed up. */
      value_list = pt_make_val_list (parser, pnode->info.sort_spec.expr);
      if (value_list == NULL)
    {
      return ER_FAILED;
    }

      if (value_tmp == NULL)
    {
      value_tmp = value_list->valp;
    }
      else
    {
      while (value_tmp->next != NULL)
        {
          value_tmp = value_tmp->next;
        }
      value_tmp->next = value_list->valp;
    }

      info->value_list->val_cnt++;
    }               /* for(pnode...) ends */

  return NO_ERROR;
}

/*
 * pt_to_instnum_pred () -
 *
 * return : XASL
 * parser (in)  :
 * xasl (in) :
 * pred (in)
 */
XASL_NODE *
pt_to_instnum_pred (PARSER_CONTEXT * parser, XASL_NODE * xasl, PT_NODE * pred)
{
  int flag = 0;

  if (xasl && pred)
    {
      xasl->instnum_pred = pt_to_pred_expr_with_arg (parser, pred, &flag);
      if (flag & PT_PRED_ARG_INSTNUM_CONTINUE)
    {
      xasl->instnum_flag |= XASL_INSTNUM_FLAG_SCAN_CONTINUE;
    }
    }

  return xasl;
}

/*
 * pt_prepare_corr_subquery_hash_result_cache ()
 * - Checks if the provided XASL node and its subqueries can be cached based on certain conditions related to predicates
 * and other flags within the structure.
 *
 * return : bool - Returns true if the XASL node and its subqueries satisfy the conditions for caching, false otherwise.
 * parser (in): PARSER_CONTEXT* - Pointer to the parser context, provides contextual information necessary for processing the node.
 * node (in): PT_NODE* - Pointer to the query node being checked. This node can represent various query elements such as expressions.
 * xasl (in) : XASL_NODE* - Pointer to the XASL node being checked for caching compatibility.
 */

int
pt_prepare_corr_subquery_hash_result_cache (PARSER_CONTEXT * parser, PT_NODE * node, XASL_NODE * xasl)
{
  int n_elements, i;
  SQ_KEY *sq_key_struct;
  QPROC_DB_VALUE_LIST dbv_list;
  bool cachable = true;

  if (XASL_IS_FLAGED (xasl, XASL_USES_SQ_CACHE))
    {
      /* No need to check twice. */
      return true;
    }

  if (node->info.query.q.select.hint & PT_HINT_NO_SUBQUERY_CACHE)
    {
      /* it means SUBQUERY RESULT won't be cached. */
      return false;
    }
  parser_walk_tree (parser, node, NULL, NULL, pt_check_corr_subquery_not_cachable_expr, &cachable);
  if (!cachable)
    {
      return false;
    }

  regu_alloc (dbv_list);
  dbv_list->val = NULL;
  dbv_list->next = NULL;

  n_elements = pt_make_sq_cache_key_struct (dbv_list, (void *) xasl, SQ_TYPE_XASL);

  if (n_elements == 0 || n_elements == ER_FAILED)
    {
      return false;
    }

  sq_key_struct = (SQ_KEY *) pt_alloc_packing_buf (sizeof (SQ_KEY));
  sq_key_struct->dbv_array = (DB_VALUE **) pt_alloc_packing_buf (n_elements * sizeof (DB_VALUE *));
  for (i = 0; i < n_elements; i++)
    {
      sq_key_struct->dbv_array[i] = dbv_list->val;
      dbv_list = dbv_list->next;
    }
  sq_key_struct->n_elements = n_elements;

  xasl->sq_cache = (SQ_CACHE *) pt_alloc_packing_buf (sizeof (SQ_CACHE));
  SQ_CACHE_KEY_STRUCT (xasl) = sq_key_struct;
  return true;
}

/*
 * pt_make_sq_cache_key_struct () - Generates a key structure for caching by recursively evaluating elements within
 * the provided XASL or predicate expressions. It constructs a complex key based on various types of elements involved in
 * the query processing, like subqueries, predicates, and regular variables.
 *
 * return : int - Returns the count of elements added to the key structure.
 * key_struct (in/out) : DB_VALUE** - Pointer to the array where generated keys are stored.
 * p (in) : void* - Generic pointer to the element being processed, which can be a XASL node, predicate, or regular variable.
 * type (in) : int - Integer representing the type of element being processed, used to determine how to handle the element.
 */

int
pt_make_sq_cache_key_struct (QPROC_DB_VALUE_LIST key_struct, void *p, int type)
{
  int cnt = 0;
  int ret;
  int i;
  XASL_NODE *xasl_src;
  XASL_NODE *scan_ptr, *aptr;
  ACCESS_SPEC_TYPE *spec;
  PRED_EXPR *pred_src;
  REGU_VARIABLE *regu_src;
  REGU_VARIABLE_LIST regu_var_list_p;
  if (!p)
    {
      return 0;
    }

  switch (type)
    {
    case SQ_TYPE_XASL:
      xasl_src = (XASL_NODE *) p;
      if (xasl_src->bptr_list || xasl_src->dptr_list || xasl_src->fptr_list || xasl_src->connect_by_ptr)
    {
      return ER_FAILED;
    }
      spec = xasl_src->spec_list;
      for (spec = xasl_src->spec_list; spec; spec = spec->next)
    {
      ret = pt_make_sq_cache_key_struct (key_struct, (void *) spec->where_key, SQ_TYPE_PRED);
      if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
      else
        {
          cnt += ret;
        }
      ret = pt_make_sq_cache_key_struct (key_struct, (void *) spec->where_pred, SQ_TYPE_PRED);
      if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
      else
        {
          cnt += ret;
        }
      ret = pt_make_sq_cache_key_struct (key_struct, (void *) spec->where_range, SQ_TYPE_PRED);
      if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
      else
        {
          cnt += ret;
        }
    }
      if (xasl_src->scan_ptr)
    {
      for (scan_ptr = xasl_src->scan_ptr; scan_ptr != NULL; scan_ptr = scan_ptr->next)
        {
          ret = pt_make_sq_cache_key_struct (key_struct, (void *) scan_ptr, SQ_TYPE_XASL);
          if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
          else
        {
          cnt += ret;
        }
        }
    }
      if (xasl_src->aptr_list)
    {
      for (aptr = xasl_src->aptr_list; aptr != NULL; aptr = aptr->next)
        {
          ret = pt_make_sq_cache_key_struct (key_struct, (void *) aptr, SQ_TYPE_XASL);
          if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
          else
        {
          cnt += ret;
        }
        }
    }
      if (xasl_src->if_pred)
    {
      ret = pt_make_sq_cache_key_struct (key_struct, (void *) xasl_src->if_pred, SQ_TYPE_PRED);
      if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
      else
        {
          cnt += ret;
        }
    }
      if (xasl_src->during_join_pred)
    {
      ret = pt_make_sq_cache_key_struct (key_struct, (void *) xasl_src->during_join_pred, SQ_TYPE_PRED);
      if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
      else
        {
          cnt += ret;
        }
    }
      if (xasl_src->after_join_pred)
    {
      ret = pt_make_sq_cache_key_struct (key_struct, (void *) xasl_src->after_join_pred, SQ_TYPE_PRED);
      if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
      else
        {
          cnt += ret;
        }
    }
      if (xasl_src->outptr_list)
    {
      regu_var_list_p = xasl_src->outptr_list->valptrp;
      for (i = 0; i < xasl_src->outptr_list->valptr_cnt; i++)
        {
          if (!regu_var_list_p)
        {
          assert (false);
          return ER_FAILED;
        }
          regu_src = &regu_var_list_p->value;
          ret = pt_make_sq_cache_key_struct (key_struct, (void *) regu_src, SQ_TYPE_REGU_VAR);
          if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
          else
        {
          cnt += ret;
        }
          regu_var_list_p = regu_var_list_p->next;
        }
    }
      if (xasl_src->type == BUILDVALUE_PROC || xasl_src->type == BUILDLIST_PROC)
    {
      AGGREGATE_TYPE *agg_list =
        (xasl_src->type ==
         BUILDVALUE_PROC) ? xasl_src->proc.buildvalue.agg_list : xasl_src->proc.buildlist.g_agg_list;
      while (agg_list)
        {
          regu_var_list_p = agg_list->operands;
          while (regu_var_list_p)
        {
          ret = pt_make_sq_cache_key_struct (key_struct, (void *) &regu_var_list_p->value, SQ_TYPE_REGU_VAR);
          if (ret == ER_FAILED)
            {
              return ER_FAILED;
            }
          else
            {
              cnt += ret;
            }
          regu_var_list_p = regu_var_list_p->next;
        }
          agg_list = agg_list->next;
        }

    }
      break;
    case SQ_TYPE_PRED:
      pred_src = (PRED_EXPR *) p;
      if (pred_src->type == T_PRED)
    {
      ret = pt_make_sq_cache_key_struct (key_struct, (void *) pred_src->pe.m_pred.lhs, SQ_TYPE_PRED);
      if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
      else
        {
          cnt += ret;
        }
      ret = pt_make_sq_cache_key_struct (key_struct, (void *) pred_src->pe.m_pred.rhs, SQ_TYPE_PRED);
      if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
      else
        {
          cnt += ret;
        }
    }
      else if (pred_src->type == T_EVAL_TERM)
    {
      COMP_EVAL_TERM t = pred_src->pe.m_eval_term.et.et_comp;
      ret = pt_make_sq_cache_key_struct (key_struct, (void *) t.lhs, SQ_TYPE_REGU_VAR);
      if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
      else
        {
          cnt += ret;
        }
      ret = pt_make_sq_cache_key_struct (key_struct, (void *) t.rhs, SQ_TYPE_REGU_VAR);
      if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
      else
        {
          cnt += ret;
        }
    }
      else if (pred_src->type == T_NOT_TERM)
    {
      ret = pt_make_sq_cache_key_struct (key_struct, (void *) pred_src->pe.m_not_term, SQ_TYPE_PRED);
      if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
      else
        {
          cnt += ret;
        }
    }
      else
    {
      return ER_FAILED;
    }
      break;
    case SQ_TYPE_REGU_VAR:
      regu_src = (REGU_VARIABLE *) p;
      switch (regu_src->type)
    {
    case TYPE_CONSTANT:
      if (!REGU_VARIABLE_IS_FLAGED (regu_src, REGU_VARIABLE_CORRELATED))
        {
          break;
        }
      ret = pt_make_sq_cache_key_struct (key_struct, (void *) regu_src->value.dbvalptr, SQ_TYPE_DBVAL);
      if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
      else
        {
          cnt += ret;
        }
      break;
    case TYPE_INARITH:
    case TYPE_OUTARITH:
      ret = pt_make_sq_cache_key_struct (key_struct, (void *) regu_src->value.arithptr->leftptr, SQ_TYPE_REGU_VAR);
      if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
      else
        {
          cnt += ret;
        }
      ret = pt_make_sq_cache_key_struct (key_struct, (void *) regu_src->value.arithptr->rightptr, SQ_TYPE_REGU_VAR);
      if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
      else
        {
          cnt += ret;
        }
      ret = pt_make_sq_cache_key_struct (key_struct, (void *) regu_src->value.arithptr->thirdptr, SQ_TYPE_REGU_VAR);
      if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
      else
        {
          cnt += ret;
        }
      break;
    case TYPE_FUNC:
      if (regu_src->value.funcp->operand)
        {
          regu_var_list_p = regu_src->value.funcp->operand;
          while (regu_var_list_p)
        {
          ret = pt_make_sq_cache_key_struct (key_struct, (void *) &regu_var_list_p->value, SQ_TYPE_REGU_VAR);
          if (ret == ER_FAILED)
            {
              return ER_FAILED;
            }
          else
            {
              cnt += ret;
            }
          regu_var_list_p = regu_var_list_p->next;
        }
        }
      break;
    case TYPE_SP:
      /* The value of regu_src->value.sp_ptr->sig->is_deterministic is interpreted as follows
       * 0: PT_AUTHID_OWNER + PT_NOT_DETERMINISTIC
       * 1: PT_AUTHID_CALLER + PT_NOT_DETERMINISTIC
       * 2: PT_AUTHID_OWNER + PT_DETERMINISTIC
       * 3: PT_AUTHID_CALLER + PT_DETERMINISTIC
       */
#if defined (CS_MODE)
      if (regu_src->value.sp_ptr->sig->is_deterministic == false)
        {
          return ER_FAILED;
        }
#endif

      regu_var_list_p = regu_src->value.sp_ptr->args;

      while (regu_var_list_p)
        {
          regu_src = &regu_var_list_p->value;
          ret = pt_make_sq_cache_key_struct (key_struct, (void *) regu_src, SQ_TYPE_REGU_VAR);
          if (ret == ER_FAILED)
        {
          return ER_FAILED;
        }
          else
        {
          cnt += ret;
        }
          regu_var_list_p = regu_var_list_p->next;
        }
      break;
    case TYPE_POSITION:
    case TYPE_LIST_ID:
      /* Currently not supported, implement later */
      return ER_FAILED;
      break;

    case TYPE_POS_VALUE:
    case TYPE_DBVAL:
    case TYPE_ORDERBY_NUM:
      /* constants */
      break;

    case TYPE_ATTR_ID:
    case TYPE_CLASS_ATTR_ID:
    case TYPE_SHARED_ATTR_ID:
      /* This is column in subquery */
      break;

    case TYPE_OID:
    case TYPE_CLASSOID:
    case TYPE_REGUVAL_LIST:
    case TYPE_REGU_VAR_LIST:
      /* Result Cache not supported */
      return ER_FAILED;
      break;

    default:
      return ER_FAILED;
      break;
    }
      break;
    case SQ_TYPE_DBVAL:
      if (key_struct)
    {
      QPROC_DB_VALUE_LIST new_dbv, list_p;
      if (key_struct->val != NULL)
        {
          list_p = key_struct;
          while (list_p->next)
        {
          list_p = list_p->next;
        }
          regu_alloc (new_dbv);
          new_dbv->next = NULL;
          new_dbv->val = (DB_VALUE *) p;
          list_p->next = new_dbv;
        }
      else
        {
          key_struct->val = (DB_VALUE *) p;
        }
    }
      cnt++;
      break;

    default:
      return ER_FAILED;
      break;
    }

  return cnt;
}

/*
 * pt_check_corr_subquery_not_cachable_expr () - Evaluates whether expressions within a node are cachable for subqueries. 
 * 
 * 
 * parser (in): PARSER_CONTEXT* - Pointer to the parser context, provides contextual information necessary for processing the node.
 * node (in): PT_NODE* - Pointer to the query node being checked. This node can represent various query elements such as expressions.
 * arg (in/out): void* - A generic pointer, which in this context is used to point to a boolean flag indicating cachability.
 * continue_walk (in/out): int* - Pointer to an integer that controls the continuation of the tree walk. Set to PT_STOP_WALK to halt further processing.
 * 
 */
static PT_NODE *
pt_check_corr_subquery_not_cachable_expr (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{

  if (node->node_type == PT_EXPR)
    {
      bool *cachable = (bool *) arg;
      switch (node->info.expr.op)
    {
    case PT_SYS_GUID:
    case PT_RAND:
    case PT_DRAND:
    case PT_RANDOM:
    case PT_DRANDOM:
    case PT_CURRENT_VALUE:
    case PT_NEXT_VALUE:
      *cachable = false;
      *continue_walk = PT_STOP_WALK;
      break;
    default:
      /* continue walk */
      break;
    }
    }
  return node;
}

static PT_NODE *
pt_make_result_ref (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * groupby_list, VAL_LIST * vallist)
{
  PT_NODE *new_node = NULL;
  PT_NODE *groupby = groupby_list;
  QPROC_DB_VALUE_LIST db_list = vallist->valp;

  if (pt_has_error (parser))
    {
      return NULL;
    }

  int is_pseudocolumn = 0;
  (void) parser_walk_tree (parser, node, pt_is_pseudocolumn_node, &is_pseudocolumn, NULL, NULL);

  if (!is_pseudocolumn && (node->node_type == PT_EXPR || node->node_type == PT_METHOD_CALL))
    {
      char *str_select = parser_print_tree (parser, node);

      for (; groupby && db_list; groupby = groupby->next, db_list = db_list->next)
    {
      char *str_group = NULL;

      if (node->node_type == PT_EXPR && groupby->info.sort_spec.expr->node_type == PT_EXPR)
        {
          /* Temporarily set paren_type for comparison if both nodes are PT_EXPR */
          int tmp_paren_type = -1;
          tmp_paren_type = groupby->info.sort_spec.expr->info.expr.paren_type;
          groupby->info.sort_spec.expr->info.expr.paren_type = node->info.expr.paren_type;

          str_group = parser_print_tree (parser, groupby->info.sort_spec.expr);

          groupby->info.sort_spec.expr->info.expr.paren_type = tmp_paren_type;
        }
      else
        {
          str_group = parser_print_tree (parser, groupby->info.sort_spec.expr);
        }

      /* brute method, compare printed trees */
      if (pt_str_compare (str_select, str_group, CASE_INSENSITIVE) == 0)
        {
          assert (node->etc == NULL);
          new_node = pt_point_ref (parser, node);
          if (new_node == NULL)
        {
          /* allocation failed */
          PT_ERROR (parser, node,
                msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC,
                        MSGCAT_SEMANTIC_OUT_OF_MEMORY));
          return NULL;
        }
          new_node->etc = (void *) db_list->val;
          break;
        }
    }
    }

  return new_node;
}

/*
 * pt_check_analytic_limit_optimization () -
 *   Check if analytic functions are optimizable for limit optimization.
 *   If optimizable, set the XASL_ANALYTIC_USES_LIMIT_OPT flag.
 * 
 * return :
 * xasl (in)  :
 * eval_list (in) :
 */
static int
pt_check_analytic_limit_optimization (XASL_NODE * xasl, ANALYTIC_EVAL_TYPE * eval_list)
{
  ANALYTIC_EVAL_TYPE *eval;
  ANALYTIC_TYPE *a_func_list;
  bool is_optimizable = true;

  if (!xasl->instnum_pred && !xasl->instnum_val)
    {
      return NO_ERROR;
    }

  if (eval_list == NULL)
    {
      return NO_ERROR;
    }

  for (eval = eval_list; eval != NULL; eval = eval->next)
    {
      if (!is_optimizable || eval->covered_size != eval->sort_list_size)
    {
      is_optimizable = false;
      break;
    }

      for (a_func_list = eval->head; a_func_list && is_optimizable; a_func_list = a_func_list->next)
    {
      switch (a_func_list->function)
        {
        case PT_FIRST_VALUE:
          if (a_func_list->ignore_nulls)
        {
          is_optimizable = false;
          break;
        }

        case PT_ROW_NUMBER:
        case PT_RANK:
        case PT_DENSE_RANK:
          break;

        default:
          is_optimizable = false;
          break;
        }
    }
    }

  if (is_optimizable)
    {
      XASL_SET_FLAG (xasl, XASL_ANALYTIC_USES_LIMIT_OPT);
    }

  return NO_ERROR;
}

static int
pt_count_analytic_covered_sort_list (PARSER_CONTEXT * parser, QO_PLAN * qo_plan, ANALYTIC_EVAL_TYPE * eval,
                     ANALYTIC_INFO * info)
{
  SORT_LIST *sort_list;
  QO_ENV *env = NULL;
  QO_INDEX_ENTRY *index_entry;
  QO_SEGMENT *seg;
  PT_NODE *attr, *node;
  int i, seg_idx, covered_count = 0;
  bool is_desc, is_desc_index;

  switch (qo_plan->plan_type)
    {
    case QO_PLANTYPE_SCAN:
      if (!qo_is_iscan (qo_plan) || (env = (qo_plan->info)->env) == NULL)
    {
      return 0;
    }
      break;

    case QO_PLANTYPE_SORT:
      if (qo_plan->plan_un.sort.subplan == NULL)
    {
      return 0;
    }
      return pt_count_analytic_covered_sort_list (parser, qo_plan->plan_un.sort.subplan, eval, info);

    case QO_PLANTYPE_JOIN:
      if (!QO_IS_NL_JOIN (qo_plan))
    {
      return 0;
    }
      return pt_count_analytic_covered_sort_list (parser, qo_plan->plan_un.join.outer, eval, info);

    case QO_PLANTYPE_FOLLOW:
    case QO_PLANTYPE_WORST:
    default:
      return 0;
    }

  assert (qo_plan->plan_un.scan.index->head);
  index_entry = qo_plan->plan_un.scan.index->head;

  is_desc_index = qo_plan->info->env->pt_tree->info.query.q.select.hint & PT_HINT_USE_IDX_DESC;

  sort_list = eval->sort_list;
  for (i = 0; i < index_entry->nsegs && sort_list; i++)
    {
      seg_idx = (index_entry->seg_idxs[i]);
      if (seg_idx == -1)
    {           /* not exist in query */
      break;
    }

      seg = QO_ENV_SEG (env, seg_idx);
      if (QO_SEG_FUNC_INDEX (seg) == true)
    {
      is_desc = index_entry->constraints->func_index_info->fi_domain->is_desc;
    }
      else
    {
      is_desc = index_entry->constraints->asc_desc[i];
    }

      if (is_desc_index)
    {
      is_desc = !is_desc;
    }

      node = QO_SEG_PT_NODE (seg);
      attr = pt_get_node_from_list (info->select_list, sort_list->pos_descr.pos_no);

      if (((sort_list->s_order == S_ASC && !is_desc) || (sort_list->s_order == S_DESC && is_desc))
      && pt_check_path_eq (parser, attr, node) == 0)
    {
      covered_count++;
    }
      else
    {
      return covered_count;
    }

      sort_list = sort_list->next;
    }

  return covered_count;
}