Skip to content

File semantic_check.c

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

/*
 * semantic_check.c - semantic checking functions
 */

#ident "$Id$"

#include "config.h"

#include <assert.h>

#include "authenticate.h"
#include "error_manager.h"
#include "parser.h"
#include "parser_message.h"
#include "semantic_check.h"
#include "memory_alloc.h"
#include "jsp_cl.h"
#include "execute_schema.h"
#include "execute_statement.h"
#include "set_object.h"
#include "schema_manager.h"
#include "release_string.h"
#include "dbi.h"
#include "xasl_generation.h"
#include "view_transform.h"
#include "show_meta.h"
#include "partition.h"
#include "db_json.hpp"
#include "object_primitive.h"
#include "db_client_type.hpp"
#include "msgcat_glossary.hpp"
#include "network_interface_cl.h"

#include "dbtype.h"
#define PT_CHAIN_LENGTH 10

typedef enum
{ PT_CAST_VALID, PT_CAST_INVALID, PT_CAST_UNSUPPORTED } PT_CAST_VAL;

typedef enum
{ PT_UNION_COMP = 1, PT_UNION_INCOMP = 0,
  PT_UNION_INCOMP_CANNOT_FIX = -1, PT_UNION_ERROR = -2
} PT_UNION_COMPATIBLE;

typedef struct seman_compatible_info
{
  int idx;
  PT_TYPE_ENUM type_enum;
  int prec;
  int scale;
  bool force_cast;
  PT_COLL_INFER coll_infer;
  const PT_NODE *ref_att;   /* column node having current compat info */
} SEMAN_COMPATIBLE_INFO;

typedef enum
{
  RANGE_MIN = 0,
  RANGE_MAX = 1
} RANGE_MIN_MAX_ENUM;

typedef struct PT_VALUE_LINKS
{
  PT_NODE *vallink;
  struct PT_VALUE_LINKS *next;
} PT_VALUE_LINKS;

typedef struct db_value_plist
{
  struct db_value_plist *next;
  DB_VALUE *val;
} DB_VALUE_PLIST;

typedef struct
{
  PT_NODE *chain[PT_CHAIN_LENGTH];
  PT_NODE **chain_ptr;
  int chain_size;
  int chain_length;
  UINTPTR spec_id;
} PT_CHAIN_INFO;

static PT_NODE *pt_derive_attribute (PARSER_CONTEXT * parser, PT_NODE * c);
static PT_NODE *pt_get_attributes (PARSER_CONTEXT * parser, const DB_OBJECT * c);
static PT_MISC_TYPE pt_get_class_type (PARSER_CONTEXT * parser, const DB_OBJECT * cls);
static int pt_number_of_attributes (PARSER_CONTEXT * parser, PT_NODE * stmt, PT_NODE ** attrs);
static int pt_is_real_class_of_vclass (PARSER_CONTEXT * parser, const PT_NODE * s_class, const PT_NODE * d_class);
static int pt_objects_assignable (PARSER_CONTEXT * parser, const PT_NODE * d_class_dt, const PT_NODE * s_class);
static int pt_class_compatible (PARSER_CONTEXT * parser, const PT_NODE * class1, const PT_NODE * class2,
                bool view_definition_context);
static bool pt_vclass_compatible (PARSER_CONTEXT * parser, const PT_NODE * att, const PT_NODE * qcol);
static int pt_type_assignable (PARSER_CONTEXT * parser, const PT_NODE * d_type, const PT_NODE * s_type);
static int pt_collection_compatible (PARSER_CONTEXT * parser, const PT_NODE * col1, const PT_NODE * col2,
                     bool view_definition_context);
static PT_UNION_COMPATIBLE pt_union_compatible (PARSER_CONTEXT * parser, PT_NODE * item1, PT_NODE * item2,
                        bool view_definition_context, bool * is_object_type);
static bool pt_is_compatible_without_cast (PARSER_CONTEXT * parser, SEMAN_COMPATIBLE_INFO * dest_sci, PT_NODE * src,
                       bool * is_cast_allowed);
static PT_NODE *pt_to_compatible_cast (PARSER_CONTEXT * parser, PT_NODE * node, SEMAN_COMPATIBLE_INFO * cinfo,
                       int num_cinfo);
static void pt_get_compatible_info_from_node (const PT_NODE * att, SEMAN_COMPATIBLE_INFO * cinfo);
static PT_NODE *pt_get_common_type_for_union (PARSER_CONTEXT * parser, PT_NODE * att1, PT_NODE * att2,
                          SEMAN_COMPATIBLE_INFO * cinfo, int idx, bool * need_cast);
static PT_NODE *pt_append_statements_on_add_attribute (PARSER_CONTEXT * parser, PT_NODE * statement_list,
                               PT_NODE * stmt_node, const char *class_name,
                               const char *attr_name, PT_NODE * attr);
#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
static PT_NODE *pt_append_statements_on_change_default (PARSER_CONTEXT * parser, PT_NODE * statement_list,
                            PT_NODE * stmt_node, const char *class_name,
                            const char *attr_name, PT_NODE * value);
static PT_NODE *pt_append_statements_on_drop_attributes (PARSER_CONTEXT * parser, PT_NODE * statement_list,
                             const char *class_name_list);
#endif
static PT_NODE *pt_values_query_to_compatible_cast (PARSER_CONTEXT * parser, PT_NODE * node,
                            SEMAN_COMPATIBLE_INFO * cinfo, int num_cinfo);
static PT_NODE *pt_make_cast_with_compatible_info (PARSER_CONTEXT * parser, PT_NODE * att, PT_NODE * next_att,
                           SEMAN_COMPATIBLE_INFO * cinfo, bool * new_cast_added);
static SEMAN_COMPATIBLE_INFO *pt_get_values_query_compatible_info (PARSER_CONTEXT * parser, PT_NODE * node,
                                   bool * need_cast);
static SEMAN_COMPATIBLE_INFO *pt_get_compatible_info (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * select_list1,
                              PT_NODE * select_list2, bool * need_cast);
static bool pt_combine_compatible_info (PARSER_CONTEXT * parser, SEMAN_COMPATIBLE_INFO * cinfo1,
                    SEMAN_COMPATIBLE_INFO * cinfo2, PT_NODE * att1, PT_NODE * att2, int index);
static bool pt_update_compatible_info (PARSER_CONTEXT * parser, SEMAN_COMPATIBLE_INFO * cinfo, PT_TYPE_ENUM common_type,
                       SEMAN_COMPATIBLE_INFO * att1_info, SEMAN_COMPATIBLE_INFO * att2_info);

#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
static PT_NODE *pt_make_parameter (PARSER_CONTEXT * parser, const char *name, int is_out_parameter);
static PT_NODE *pt_append_statements_on_insert (PARSER_CONTEXT * parser, PT_NODE * stmt_node, const char *class_name,
                        const char *attr_name, PT_NODE * value, PT_NODE * parameter);
static PT_NODE *pt_append_statements_on_update (PARSER_CONTEXT * parser, PT_NODE * stmt_node, const char *class_name,
                        const char *attr_name, const char *alias_name, PT_NODE * value,
                        PT_NODE ** where_ptr);
static PT_NODE *pt_append_statements_on_delete (PARSER_CONTEXT * parser, PT_NODE * stmt_node, const char *class_name,
                        const char *attr_name, const char *alias_name, PT_NODE ** where_ptr);
static void pt_resolve_insert_external (PARSER_CONTEXT * parser, PT_NODE * insert);
static void pt_resolve_update_external (PARSER_CONTEXT * parser, PT_NODE * update);
static void pt_resolve_delete_external (PARSER_CONTEXT * parser, PT_NODE * delete);
static PT_NODE *pt_make_default_value (PARSER_CONTEXT * parser, const char *class_name, const char *attr_name);
#endif /* ENABLE_UNUSED_FUNCTION */
static void pt_resolve_default_external (PARSER_CONTEXT * parser, PT_NODE * alter);
static PT_NODE *pt_check_data_default (PARSER_CONTEXT * parser, PT_NODE * data_default_list);
static PT_NODE *pt_find_query (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk);
static PT_NODE *pt_find_default_expression (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk);
static PT_NODE *pt_find_aggregate_function (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk);
static PT_NODE *pt_find_aggregate_analytic_pre (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk);
static PT_NODE *pt_find_aggregate_analytic_post (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg,
                         int *continue_walk);
static PT_NODE *pt_find_aggregate_analytic_in_where (PARSER_CONTEXT * parser, PT_NODE * node);
static PT_NODE *pt_find_method_call (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk);
static void pt_check_attribute_domain (PARSER_CONTEXT * parser, PT_NODE * attr_defs, PT_MISC_TYPE class_type,
                       const char *self, const bool reuse_oid, PT_NODE * stmt);
static void pt_check_mutable_attributes (PARSER_CONTEXT * parser, DB_OBJECT * cls, PT_NODE * attr_defs);
static void pt_check_alter (PARSER_CONTEXT * parser, PT_NODE * alter);
static const char *attribute_name (PARSER_CONTEXT * parser, PT_NODE * att);
static int is_shared_attribute (PARSER_CONTEXT * parser, PT_NODE * att);
static int pt_find_partition_column_count_func (PT_NODE * func, PT_NODE ** name_node);
static int pt_find_partition_column_count (PT_NODE * expr, PT_NODE ** name_node);
static int pt_value_links_add (PARSER_CONTEXT * parser, PT_NODE * val, PT_NODE * parts, PT_VALUE_LINKS * ptl);

static int pt_check_partition_values (PARSER_CONTEXT * parser, PT_TYPE_ENUM desired_type, PT_NODE * data_type,
                      PT_VALUE_LINKS * ptl, PT_NODE * parts);
static void pt_check_partitions (PARSER_CONTEXT * parser, PT_NODE * stmt, MOP dbobj);
static int partition_range_min_max (DB_VALUE ** dest, DB_VALUE * inval, int min_max);
static int db_value_list_add (DB_VALUE_PLIST ** ptail, DB_VALUE * val);
static int db_value_list_find (const DB_VALUE_PLIST * phead, const DB_VALUE * val);
static int db_value_list_finddel (DB_VALUE_PLIST ** phead, DB_VALUE * val);
static void pt_check_alter_partition (PARSER_CONTEXT * parser, PT_NODE * stmt, MOP dbobj);
static bool pt_attr_refers_to_self (PARSER_CONTEXT * parser, PT_NODE * attr, const char *self);
static bool pt_is_compatible_type (const PT_TYPE_ENUM arg1_type, const PT_TYPE_ENUM arg2_type);
static PT_UNION_COMPATIBLE pt_check_vclass_attr_qspec_compatible (PARSER_CONTEXT * parser, PT_NODE * attr,
                                  PT_NODE * col);
static PT_NODE *pt_check_vclass_query_spec (PARSER_CONTEXT * parser, PT_NODE * qry, PT_NODE * attrs, const char *self,
                        const bool do_semantic_check);
static PT_NODE *pt_type_cast_vclass_query_spec (PARSER_CONTEXT * parser, PT_NODE * qry, PT_NODE * attrs);
static PT_NODE *pt_check_default_vclass_query_spec (PARSER_CONTEXT * parser, PT_NODE * qry, PT_NODE * attrs);
static void pt_check_create_view (PARSER_CONTEXT * parser, PT_NODE * stmt);
static void pt_check_create_entity (PARSER_CONTEXT * parser, PT_NODE * node);
static void pt_check_create_user (PARSER_CONTEXT * parser, PT_NODE * node);
static void pt_check_create_index (PARSER_CONTEXT * parser, PT_NODE * node);
static void pt_check_alter_synonym (PARSER_CONTEXT * parser, PT_NODE * node);
static void pt_check_create_synonym (PARSER_CONTEXT * parser, PT_NODE * node);
static void pt_check_drop_synonym (PARSER_CONTEXT * parser, PT_NODE * node);
static void pt_check_rename_synonym (PARSER_CONTEXT * parser, PT_NODE * node);
static void pt_check_create_stored_procedure (PARSER_CONTEXT * parser, PT_NODE * node);
static void pt_check_drop (PARSER_CONTEXT * parser, PT_NODE * node);
static void pt_check_grant_revoke (PARSER_CONTEXT * parser, PT_NODE * node);
static void pt_check_method (PARSER_CONTEXT * parser, PT_NODE * node);
static void pt_check_truncate (PARSER_CONTEXT * parser, PT_NODE * node);
static void pt_check_kill (PARSER_CONTEXT * parser, PT_NODE * node);
static void pt_check_alter_serial (PARSER_CONTEXT * parser, PT_NODE * node);
static void pt_check_update_stats (PARSER_CONTEXT * parser, PT_NODE * node);
static PT_NODE *pt_check_single_valued_node (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_check_single_valued_node_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg,
                          int *continue_walk);
static void pt_check_into_clause (PARSER_CONTEXT * parser, PT_NODE * qry);
static int pt_normalize_path (PARSER_CONTEXT * parser, REFPTR (char, c));
static int pt_json_str_codeset_normalization (PARSER_CONTEXT * parser, REFPTR (char, c));
static int pt_check_json_table_node (PARSER_CONTEXT * parser, PT_NODE * node);
static PT_NODE *pt_semantic_check_local (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_gen_isnull_preds (PARSER_CONTEXT * parser, PT_NODE * pred, PT_CHAIN_INFO * chain);
static PT_NODE *pt_path_chain (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_expand_isnull_preds_helper (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_expand_isnull_preds (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_check_and_replace_hostvar (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_check_with_clause (PARSER_CONTEXT * parser, PT_NODE * node);
static PT_NODE *pt_check_with_info (PARSER_CONTEXT * parser, PT_NODE * node, SEMANTIC_CHK_INFO * info);
static DB_OBJECT *pt_find_class (PARSER_CONTEXT * parser, PT_NODE * p, bool for_update);
static void pt_check_unique_attr (PARSER_CONTEXT * parser, const char *entity_name, PT_NODE * att,
                  PT_NODE_TYPE att_type);
static void pt_check_function_index_expr (PARSER_CONTEXT * parser, PT_NODE * node);
static PT_NODE *pt_check_function_index_expr_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg,
                          int *continue_walk);
static void pt_check_assignments (PARSER_CONTEXT * parser, PT_NODE * stmt);
static PT_NODE *pt_replace_names_in_update_values (PARSER_CONTEXT * parser, PT_NODE * update);
static PT_NODE *pt_replace_referenced_attributes (PARSER_CONTEXT * parser, PT_NODE * node, void *arg,
                          int *continue_walk);
static PT_NODE *pt_coerce_insert_values (PARSER_CONTEXT * parser, PT_NODE * stmt);
static void pt_check_xaction_list (PARSER_CONTEXT * parser, PT_NODE * node);
static PT_NODE *pt_count_iso_nodes (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_count_time_nodes (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_check_isolation_lvl (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_check_constraint (PARSER_CONTEXT * parser, const PT_NODE * create, const PT_NODE * constraint);
static PT_NODE *pt_check_constraints (PARSER_CONTEXT * parser, const PT_NODE * create);
static int pt_check_auto_increment_table_option (PARSER_CONTEXT * parser, PT_NODE * create);
static DB_OBJECT *pt_check_user_exists (PARSER_CONTEXT * parser, PT_NODE * cls_ref);
static int pt_collection_assignable (PARSER_CONTEXT * parser, const PT_NODE * d_col, const PT_NODE * s_col);
static int pt_assignment_class_compatible (PARSER_CONTEXT * parser, PT_NODE * lhs, PT_NODE * rhs);
static PT_NODE *pt_assignment_compatible (PARSER_CONTEXT * parser, PT_NODE * lhs, PT_NODE * rhs);
static int pt_check_defaultf (PARSER_CONTEXT * parser, PT_NODE * node);
static PT_NODE *pt_check_vclass_union_spec (PARSER_CONTEXT * parser, PT_NODE * qry, PT_NODE * attrds);
static int pt_check_group_concat_order_by (PARSER_CONTEXT * parser, PT_NODE * func);
static bool pt_has_parameters (PARSER_CONTEXT * parser, PT_NODE * stmt);
static PT_NODE *pt_is_parameter_node (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_resolve_sort_spec_expr (PARSER_CONTEXT * parser, PT_NODE * sort_spec, PT_NODE * select_list);
static PT_NODE *pt_find_matching_sort_spec (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * spec_list,
                        PT_NODE * select_list);
static PT_NODE *pt_remove_unusable_sort_specs (PARSER_CONTEXT * parser, PT_NODE * list);
static PT_NODE *pt_check_analytic_function (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_check_filter_index_expr_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_check_filter_index_expr_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg,
                         int *continue_walk);
static void pt_check_filter_index_expr (PARSER_CONTEXT * parser, PT_NODE * atts, PT_NODE * node, MOP db_obj);
static PT_NODE *pt_check_sub_insert (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk);
static PT_NODE *pt_get_assignments (PT_NODE * node, bool * dblinked);
static int pt_check_cume_dist_percent_rank_order_by (PARSER_CONTEXT * parser, PT_NODE * func);
static PT_UNION_COMPATIBLE pt_get_select_list_coll_compat (PARSER_CONTEXT * parser, PT_NODE * query,
                               SEMAN_COMPATIBLE_INFO * cinfo, int num_cinfo);
static PT_UNION_COMPATIBLE pt_apply_union_select_list_collation (PARSER_CONTEXT * parser, PT_NODE * query,
                                 SEMAN_COMPATIBLE_INFO * cinfo, int num_cinfo);
static PT_NODE *pt_mark_union_leaf_nodes (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);

static void pt_check_vacuum (PARSER_CONTEXT * parser, PT_NODE * node);

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

static int pt_check_range_partition_strict_increasing (PARSER_CONTEXT * parser, PT_NODE * stmt, PT_NODE * part,
                               PT_NODE * part_next, PT_NODE * column_dt);
static int pt_coerce_partition_value_with_data_type (PARSER_CONTEXT * parser, PT_NODE * value, PT_NODE * data_type);
static int pt_check_default_value_param_for_stored_procedure (PARSER_CONTEXT * parser, PT_NODE * param);

/* pt_combine_compatible_info () - combine two cinfo into cinfo1
 *   return: true if compatible, else false
 *   cinfo1(in);
 *   cinfo2(in):
 *   att1(in):
 *   att2(in):
 *   index(in): for update the idx of cinfo1
 */
static bool
pt_combine_compatible_info (PARSER_CONTEXT * parser, SEMAN_COMPATIBLE_INFO * cinfo1, SEMAN_COMPATIBLE_INFO * cinfo2,
                PT_NODE * att1, PT_NODE * att2, int index)
{
  PT_TYPE_ENUM common_type;
  bool is_compatible = false;

  assert (parser != NULL && cinfo1 != NULL && cinfo2 != NULL);
  assert (att1 != NULL && att2 != NULL);

  /* init cinfo before combine */
  if (cinfo1->idx == -1 && cinfo2->idx == -1)
    {
      return true;
    }

  if (cinfo1->idx == -1)
    {
      cinfo1->idx = index;
      pt_get_compatible_info_from_node (att1, cinfo1);
    }

  if (cinfo2->idx == -1)
    {
      cinfo2->idx = index;
      pt_get_compatible_info_from_node (att2, cinfo2);
    }

  common_type = pt_common_type (cinfo1->type_enum, cinfo2->type_enum);
  is_compatible = pt_update_compatible_info (parser, cinfo1, common_type, cinfo1, cinfo2);

  return is_compatible;
}

/* pt_update_compatible_info () - update cinfo after get common type for cast
 *   return: true if compatible, else false
 *   cinfo(in);
 *   common_type(in):
 *   int p1, s1, p2, s2: prec1,scale1,prec2 and scale2
 */
static bool
pt_update_compatible_info (PARSER_CONTEXT * parser, SEMAN_COMPATIBLE_INFO * cinfo, PT_TYPE_ENUM common_type,
               SEMAN_COMPATIBLE_INFO * att1_info, SEMAN_COMPATIBLE_INFO * att2_info)
{
  bool is_compatible = false;

  assert (parser != NULL && cinfo != NULL);
  assert (att1_info != NULL && att2_info != NULL);

  switch (common_type)
    {
    case PT_TYPE_CHAR:
    case PT_TYPE_VARCHAR:
    case PT_TYPE_BIT:
    case PT_TYPE_VARBIT:
      is_compatible = true;

      if (common_type == PT_TYPE_CHAR || common_type == PT_TYPE_VARCHAR)
    {
      cinfo->type_enum = PT_TYPE_VARCHAR;
    }
      else
    {
      cinfo->type_enum = PT_TYPE_VARBIT;
    }

      if (att1_info->prec == DB_DEFAULT_PRECISION || att2_info->prec == DB_DEFAULT_PRECISION)
    {
      cinfo->prec = DB_DEFAULT_PRECISION;
    }
      else
    {
      cinfo->prec = MAX (att1_info->prec, att2_info->prec);
      if (cinfo->prec == 0)
        {
          cinfo->prec = DB_DEFAULT_PRECISION;
        }
    }
      break;

    case PT_TYPE_NUMERIC:
      is_compatible = true;

      cinfo->type_enum = common_type;

      cinfo->scale = MAX (att1_info->scale, att2_info->scale);
      cinfo->prec = MAX ((att1_info->prec - att1_info->scale), (att2_info->prec - att2_info->scale)) + cinfo->scale;

      if (cinfo->prec > DB_MAX_NUMERIC_PRECISION)
    {           /* overflow */
      cinfo->scale -= (cinfo->prec - DB_MAX_NUMERIC_PRECISION);
      if (cinfo->scale < 0)
        {
          cinfo->scale = 0;
        }
      cinfo->prec = DB_MAX_NUMERIC_PRECISION;
    }
      break;

    case PT_TYPE_SET:
    case PT_TYPE_MULTISET:
    case PT_TYPE_SEQUENCE:
      /* NEVER try to fix set types */
      is_compatible = true;
      break;
    case PT_TYPE_NONE:
      is_compatible = false;
      break;

    default:
      is_compatible = true;
      cinfo->type_enum = common_type;
      break;
    }

  return is_compatible;
}

/* pt_values_query_to_compatible_cast () - cast select list with cinfo
 *   return:
 *   parser(in):
 *   node(in):
 *   cinfo(in):
 */
static PT_NODE *
pt_values_query_to_compatible_cast (PARSER_CONTEXT * parser, PT_NODE * node, SEMAN_COMPATIBLE_INFO * cinfo,
                    int num_cinfo)
{
  PT_NODE *node_list, *result = node;
  int i;
  PT_NODE *attrs, *att;
  PT_NODE *prev_att, *next_att, *new_att = NULL;
  bool new_cast_added;
  bool need_to_cast;
  PT_NODE_LIST_INFO *cur_node_list_info;
  bool is_select_data_type_set = false;

  assert (parser != NULL && node != NULL && cinfo != NULL);
  assert (node->node_type == PT_SELECT && PT_IS_VALUE_QUERY (node));

  node_list = node->info.query.q.select.list;
  assert (node_list);

  for (; node_list; node_list = node_list->next)
    {
      cur_node_list_info = &node_list->info.node_list;
      attrs = cur_node_list_info->list;

      prev_att = NULL;
      for (att = attrs, i = 0; i < num_cinfo && att != NULL; ++i, att = next_att)
    {
      bool is_cast_allowed = true;

      new_cast_added = false;
      next_att = att->next;
      need_to_cast = false;
      if (cinfo[i].idx == i)
        {
          if (!pt_is_compatible_without_cast (parser, &(cinfo[i]), att, &is_cast_allowed))
        {
          need_to_cast = true;
          if ((PT_IS_STRING_TYPE (att->type_enum) || att->type_enum == PT_TYPE_NUMERIC)
              && att->data_type == NULL)
            {
              result = NULL;
              goto end;
            }
        }
        }

      if (need_to_cast)
        {
          if (!is_cast_allowed)
        {
          result = NULL;
          goto end;
        }

          new_att = pt_make_cast_with_compatible_info (parser, att, next_att, cinfo + i, &new_cast_added);
          if (new_att == NULL)
        {
          goto out_of_mem;
        }

          if (new_cast_added)
        {
          att = new_att;
        }

          if (prev_att == NULL)
        {
          cur_node_list_info->list = att;
          if (is_select_data_type_set == false)
            {
              node->type_enum = att->type_enum;
              if (node->data_type)
            {
              parser_free_tree (parser, node->data_type);
            }
              node->data_type = parser_copy_tree_list (parser, att->data_type);
              is_select_data_type_set = true;
            }
        }
          else
        {
          prev_att->next = att;
        }
        }
      prev_att = att;
    }
    }

end:
  return result;

out_of_mem:
  PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
  return NULL;
}

/*
 * pt_get_compatible_info () -
 *   return:  return a pointer of SEMAN_COMPATIBLE_INFO array or null on error
 *   parser(in): parser context
 *   node(in): values query node or union
 *   select_list1(in):
 *   select_list2(in):
 *   need_cast(in/out):
 *   current_row(in):  for value query error info
 *   note: the return pointer must be freed by free_and_init
 */
static SEMAN_COMPATIBLE_INFO *
pt_get_compatible_info (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * select_list1, PT_NODE * select_list2,
            bool * need_cast)
{
  PT_NODE *att1, *att2, *p, *q;
  PT_UNION_COMPATIBLE compatible;
  SEMAN_COMPATIBLE_INFO *cinfo = NULL, *result = NULL;
  bool is_object_type;
  int i, k;
  int cnt1, cnt2;
  bool need_cast_tmp = false;

  assert (parser != NULL && node != NULL && select_list1 != NULL && select_list2 != NULL && need_cast != NULL);

  *need_cast = false;

  p = select_list1;
  q = select_list2;

  if (p->node_type == PT_NODE_LIST)
    {
      assert (q->node_type == PT_NODE_LIST);

      select_list1 = p->info.node_list.list;
      select_list2 = q->info.node_list.list;
    }

  cnt1 = pt_length_of_select_list (select_list1, EXCLUDE_HIDDEN_COLUMNS);
  cnt2 = pt_length_of_select_list (select_list2, EXCLUDE_HIDDEN_COLUMNS);

  if (cnt1 != cnt2)
    {
      if (select_list1->node_type == PT_NODE_LIST)
    {
      PT_ERRORmf4 (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ARITY_OF_VALUES_CLAUSE_MISMATCH, cnt1,
               pt_short_print (parser, p), cnt2, pt_short_print (parser, q));
    }
      else
    {
      PT_ERRORmf2 (parser, select_list1, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ARITY_MISMATCH, cnt1, cnt2);
    }
      goto end;
    }

  /* compare the columns */
  for (i = 0, att1 = select_list1, att2 = select_list2; i < cnt1; ++i, att1 = att1->next, att2 = att2->next)
    {
      compatible = pt_union_compatible (parser, att1, att2, false, &is_object_type);
      if (compatible == PT_UNION_INCOMP)
    {
      /* alloc compatible info array */
      if (cinfo == NULL)
        {
          cinfo = (SEMAN_COMPATIBLE_INFO *) malloc (cnt1 * sizeof (SEMAN_COMPATIBLE_INFO));
          if (cinfo == NULL)
        {
          goto out_of_mem;
        }

          for (k = 0; k < cnt1; ++k)
        {
          cinfo[k].idx = -1;
          cinfo[k].type_enum = PT_TYPE_NONE;
          cinfo[k].prec = DB_DEFAULT_PRECISION;
          cinfo[k].scale = DB_DEFAULT_SCALE;
          cinfo[k].force_cast = false;
          cinfo[k].coll_infer.coll_id = LANG_SYS_COLLATION;
          cinfo[k].coll_infer.codeset = LANG_SYS_CODESET;
          cinfo[k].coll_infer.coerc_level = PT_COLLATION_NOT_COERC;
          cinfo[k].coll_infer.can_force_cs = false;
          cinfo[k].ref_att = NULL;
        }
        }

      if (pt_get_common_type_for_union (parser, att1, att2, &cinfo[i], i, &need_cast_tmp) == NULL)
        {
          goto end;
        }
      *need_cast |= need_cast_tmp;
    }
      else if (compatible == PT_UNION_INCOMP_CANNOT_FIX || compatible == PT_UNION_ERROR)
    {
      if (!is_object_type)
        {
          PT_ERRORmf2 (parser, att1, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UNION_INCOMPATIBLE,
               pt_short_print (parser, att1), pt_short_print (parser, att2));
          goto end;
        }
    }
    }

  result = cinfo;

end:
  if (result != cinfo && cinfo != NULL)
    {
      free_and_init (cinfo);
    }
  return result;

out_of_mem:
  PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
  return NULL;
}

/*
 * pt_make_cast_with_compatible_info () -
 *   return:  return a pointer to PT_EXPR node (cast)
 *   parser(in): parser context
 *   att(in): current attribute
 *   next_att(in): next attribute
 *   cinfo(in): compatible info
 *   new_cast_added(out): true if a new PT_EXPR node for cast if allocated, else false
 */
static PT_NODE *
pt_make_cast_with_compatible_info (PARSER_CONTEXT * parser, PT_NODE * att, PT_NODE * next_att,
                   SEMAN_COMPATIBLE_INFO * cinfo, bool * new_cast_added)
{
  PT_EXPR_INFO *temp_expr;
  PT_DATA_TYPE_INFO *temp_data_type;
  PT_NODE *new_att = NULL, *new_dt = NULL;

  assert (parser != NULL && att != NULL && cinfo != NULL && new_cast_added != NULL);

  *new_cast_added = false;
  if (att->node_type == PT_EXPR && att->info.expr.op == PT_CAST && att->etc != NULL)
    {
      /* system added cast operator */
      temp_expr = &att->info.expr;
      temp_expr->cast_type->type_enum = cinfo->type_enum;

      temp_data_type = &temp_expr->cast_type->info.data_type;
      temp_data_type->precision = cinfo->prec;
      temp_data_type->dec_precision = cinfo->scale;
      if (PT_HAS_COLLATION (cinfo->type_enum))
    {
      temp_data_type->collation_id = cinfo->coll_infer.coll_id;
      temp_data_type->units = (int) cinfo->coll_infer.codeset;
    }

      att->type_enum = att->info.expr.cast_type->type_enum;
      att->data_type->type_enum = cinfo->type_enum;
      temp_data_type = &att->data_type->info.data_type;
      temp_data_type->precision = cinfo->prec;
      temp_data_type->dec_precision = cinfo->scale;
      if (PT_HAS_COLLATION (cinfo->type_enum))
    {
      temp_data_type->collation_id = cinfo->coll_infer.coll_id;
      temp_data_type->units = (int) cinfo->coll_infer.codeset;
    }

      new_att = att;
    }
  else
    {
      /* create new cast node */
      att->next = NULL;

      new_att = parser_new_node (parser, PT_EXPR);
      new_dt = parser_new_node (parser, PT_DATA_TYPE);
      if (new_att == NULL || new_dt == NULL)
    {
      if (new_att)
        {
          parser_free_tree (parser, new_att);
          new_att = NULL;
        }
      if (new_dt)
        {
          parser_free_tree (parser, new_dt);
          new_dt = NULL;
        }

      goto out_of_mem;
    }

      /* move alias */
      new_att->line_number = att->line_number;
      new_att->column_number = att->column_number;
      if (att->alias_print == NULL && att->node_type == PT_NAME)
    {
      new_att->alias_print = att->info.name.original;
    }
      else
    {
      new_att->alias_print = att->alias_print;
    }
      att->alias_print = NULL;

      new_dt->type_enum = cinfo->type_enum;
      temp_data_type = &new_dt->info.data_type;
      temp_data_type->precision = cinfo->prec;
      temp_data_type->dec_precision = cinfo->scale;
      if (PT_HAS_COLLATION (cinfo->type_enum))
    {
      temp_data_type->collation_id = cinfo->coll_infer.coll_id;
      temp_data_type->units = (int) cinfo->coll_infer.codeset;
    }

      new_att->type_enum = new_dt->type_enum;
      temp_expr = &new_att->info.expr;
      temp_expr->op = PT_CAST;
      temp_expr->cast_type = new_dt;
      temp_expr->arg1 = att;
      new_att->next = next_att;
      new_att->etc = cinfo; /* to make this as system added */
      new_att->flag.is_value_query = att->flag.is_value_query;

      new_att->data_type = parser_copy_tree_list (parser, new_dt);
      PT_EXPR_INFO_SET_FLAG (new_att, PT_EXPR_INFO_CAST_SHOULD_FOLD);

      *new_cast_added = true;
    }

  return new_att;

out_of_mem:
  /* the error msg will be set in the caller */
  return NULL;
}

/*
 * pt_get_values_query_compatible_info () -
 *   return:  return a pointer of SEMAN_COMPATIBLE_INFO array or null on error
 *   parser(in): parser context
 *   node(in): values query node
 *   need_cast(in/out):
 *
 *   note: the return pointer must be freed by free_and_init
 */
static SEMAN_COMPATIBLE_INFO *
pt_get_values_query_compatible_info (PARSER_CONTEXT * parser, PT_NODE * node, bool * need_cast)
{
  PT_NODE *attrs1, *attrs2, *att1, *att2, *node_list, *next_node_list;
  SEMAN_COMPATIBLE_INFO *cinfo = NULL, *cinfo_cur = NULL, *result = NULL;
  int i, count;
  bool is_compatible, need_cast_cond = false;

  assert (parser);

  *need_cast = false;

  if (node == NULL || node->node_type != PT_SELECT || !PT_IS_VALUE_QUERY (node))
    {
      return NULL;
    }

  node_list = node->info.query.q.select.list;
  if (node_list == NULL)
    {
      return NULL;
    }
  attrs1 = node_list->info.node_list.list;

  next_node_list = node_list->next;
  /* only one row */
  if (next_node_list == NULL)
    {
      return NULL;
    }
  attrs2 = next_node_list->info.node_list.list;

  count = pt_length_of_select_list (attrs1, EXCLUDE_HIDDEN_COLUMNS);

  /* get compatible_info for cast */
  while (next_node_list != NULL)
    {
      cinfo_cur = pt_get_compatible_info (parser, node, node_list, next_node_list, &need_cast_cond);
      if (cinfo_cur == NULL && need_cast_cond)
    {
      goto end;
    }

      if (cinfo == NULL)
    {
      cinfo = cinfo_cur;
      *need_cast = need_cast_cond;
      cinfo_cur = NULL; /* do not free */
    }
      else
    {
      /* compare cinfo[i].type_enum and cinfo_cur[i].type_enum and save the compatible type_enum in cinfo[i] */
      if (need_cast_cond)
        {
          is_compatible = true;
          for (i = 0, att1 = attrs1, att2 = attrs2; i < count; ++i, att1 = att1->next, att2 = att2->next)
        {
          if (cinfo_cur[i].idx == -1)
            {
              continue;
            }

          if (cinfo[i].idx == -1)
            {
              cinfo[i] = cinfo_cur[i];
              *need_cast = true;
              continue;
            }

          /* if equal, skip the following steps */
          if (cinfo[i].type_enum == cinfo_cur[i].type_enum && cinfo[i].prec == cinfo_cur[i].prec
              && cinfo[i].scale == cinfo_cur[i].scale
              && cinfo[i].coll_infer.coll_id == cinfo_cur[i].coll_infer.coll_id)
            {
              assert (cinfo[i].idx == cinfo_cur[i].idx);
              continue;
            }

          /* combine the two cinfos */
          is_compatible = pt_combine_compatible_info (parser, cinfo + i, cinfo_cur + i, att1, att2, i);
          if (!is_compatible)
            {
              break;
            }

          *need_cast = true;
        }

          if (!is_compatible)
        {
          PT_ERRORmf2 (parser, att1, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UNION_INCOMPATIBLE,
                   pt_short_print (parser, att1), pt_short_print (parser, att2));
          goto end;
        }
        }
    }

      attrs1 = attrs2;
      node_list = next_node_list;
      next_node_list = next_node_list->next;
      if (next_node_list != NULL)
    {
      attrs2 = next_node_list->info.node_list.list;
    }

      /* free cinfo_cur */
      free_and_init (cinfo_cur);
    }

  /* assign cinfo to return value */
  result = cinfo;

end:
  if (cinfo_cur != NULL)
    {
      free_and_init (cinfo_cur);
    }
  if (result != cinfo && cinfo != NULL)
    {
      free_and_init (cinfo);
    }
  return result;
}

/*
 * pt_check_compatible_node_for min(), max()
 */
bool
pt_check_compatible_node_for_min_max_optimize (PARSER_CONTEXT * parser, PT_NODE * order, PT_NODE * column)
{
  PT_NODE *arg1;
  PT_TYPE_ENUM type1;

  /* only min(), max() is allowed */
  if (order == NULL || column == NULL || order->node_type != PT_FUNCTION)
    {
      return false;
    }

  if (order->info.function.function_type != PT_MIN && order->info.function.function_type != PT_MAX)
    {
      return false;
    }

  arg1 = order->info.function.arg_list;
  if (arg1->node_type != column->node_type || arg1->node_type != PT_NAME)
    {
      return false;
    }

  if (pt_check_path_eq (parser, arg1, column) != 0)
    {
      return false;
    }

  /* only numeric, string, date-time type is allowed
   * Only string type : Do not consider 'CAST (enum_col as VARCHAR)' equal to 'enum_col' */
  type1 = arg1->type_enum;

  if (PT_IS_NUMERIC_TYPE (type1) || PT_IS_STRING_TYPE (type1) || PT_IS_DATE_TIME_TYPE (type1))
    {
      return true;
    }

  return false;
}

/*
 * pt_check_compatible_node_for_orderby ()
 */
bool
pt_check_compatible_node_for_orderby (PARSER_CONTEXT * parser, PT_NODE * order, PT_NODE * column)
{
  PT_NODE *arg1, *cast_type;
  PT_TYPE_ENUM type1, type2;

  if (order == NULL || column == NULL || order->node_type != PT_EXPR || order->info.expr.op != PT_CAST)
    {
      return false;
    }

  arg1 = order->info.expr.arg1;
  if (arg1->node_type != column->node_type)
    {
      return false;
    }

  if (arg1->node_type != PT_NAME && arg1->node_type != PT_DOT_)
    {
      return false;
    }

  if (pt_check_path_eq (parser, arg1, column) != 0)
    {
      return false;
    }

  cast_type = order->info.expr.cast_type;
  assert (cast_type != NULL);

  type1 = arg1->type_enum;
  type2 = cast_type->type_enum;

  if (PT_IS_NUMERIC_TYPE (type1) && PT_IS_NUMERIC_TYPE (type2))
    {
      return true;
    }

  /* Only string type : Do not consider 'CAST (enum_col as VARCHAR)' equal to 'enum_col' */
  if (PT_IS_STRING_TYPE (type1) && PT_IS_STRING_TYPE (type2))
    {
      int c1, c2;

      c1 = arg1->data_type->info.data_type.collation_id;
      c2 = cast_type->info.data_type.collation_id;
      return c1 == c2;
    }

  if (PT_IS_DATE_TIME_TYPE (type1) && PT_IS_DATE_TIME_TYPE (type2))
    {
      if ((type1 == PT_TYPE_TIME && type2 != PT_TYPE_TIME) || (type1 != PT_TYPE_TIME && type2 == PT_TYPE_TIME))
    {
      return false;
    }
      return true;
    }

  return false;
}

/*
 * pt_check_cast_op () - Checks to see if the cast operator is well-formed
 *   return: none
 *   parser(in):
 *   node(in): the node to check
 */
bool
pt_check_cast_op (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *arg1;
  PT_TYPE_ENUM cast_type = PT_TYPE_NONE, arg_type;
  PT_CAST_VAL cast_is_valid = PT_CAST_VALID;

  if (node == NULL || node->node_type != PT_EXPR || node->info.expr.op != PT_CAST)
    {
      /* this should not happen, but don't crash and burn if it does */
      assert (false);
      return false;
    }

  /* check special CAST : COLLATE modifier */
  if (PT_EXPR_INFO_IS_FLAGED (node, PT_EXPR_INFO_CAST_COLL_MODIFIER))
    {
      LANG_COLLATION *lc;
      PT_NODE *arg_dt = NULL;

      if (node->info.expr.arg1 != NULL && node->info.expr.arg1->type_enum != PT_TYPE_NONE
      && !PT_HAS_COLLATION (node->info.expr.arg1->type_enum))
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_COLLATE_NOT_ALLOWED);
      return false;
    }
      /* arg1 may have been set NULL due to previous semantic errors */
      arg_dt = (node->info.expr.arg1 != NULL) ? node->info.expr.arg1->data_type : NULL;

      lc = lang_get_collation (PT_GET_COLLATION_MODIFIER (node));
      if (arg_dt != NULL && lc->codeset != arg_dt->info.data_type.units)
    {
      /* cannot change codeset with COLLATE */
      PT_ERRORmf2 (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CS_MATCH_COLLATE,
               lang_get_codeset_name (arg_dt->info.data_type.units), lang_get_codeset_name (lc->codeset));
      return false;
    }
    }

  /* get cast type */
  if (node->info.expr.cast_type != NULL)
    {
      cast_type = node->info.expr.cast_type->type_enum;
    }
  else
    {
      if (!pt_has_error (parser) && !PT_EXPR_INFO_IS_FLAGED (node, PT_EXPR_INFO_CAST_COLL_MODIFIER))
    {
      PT_INTERNAL_ERROR (parser, "null cast type");
    }
      return false;
    }

  /* get argument */
  arg1 = node->info.expr.arg1;
  if (arg1 == NULL)
    {
      /* a parse error might have occurred lower in the parse tree of arg1; don't register another error unless no
       * error has been set */

      if (!pt_has_error (parser))
    {
      PT_ERRORmf2 (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CANT_COERCE_TO, "(null)",
               pt_show_type_enum (cast_type));
    }
      return false;
    }

  /* CAST (arg_type AS cast_type) */
  if (arg1->node_type == PT_EXPR && arg1->info.expr.op == PT_CAST)
    {
      /* arg1 is a cast, so arg1.type_enum is not yet set; pull type from expression's cast type */
      arg_type = arg1->info.expr.cast_type->type_enum;
    }
  else
    {
      /* arg1 is not a cast */
      arg_type = arg1->type_enum;
    }

  switch (arg_type)
    {
    case PT_TYPE_INTEGER:
    case PT_TYPE_BIGINT:
    case PT_TYPE_FLOAT:
    case PT_TYPE_DOUBLE:
    case PT_TYPE_SMALLINT:
    case PT_TYPE_MONETARY:
    case PT_TYPE_NUMERIC:
      switch (cast_type)
    {
    case PT_TYPE_BIT:
    case PT_TYPE_VARBIT:
    case PT_TYPE_DATE:
      /* allow numeric to TIME and TIMESTAMP conversions */
    case PT_TYPE_DATETIME:
    case PT_TYPE_DATETIMETZ:
    case PT_TYPE_DATETIMELTZ:
    case PT_TYPE_SET:
    case PT_TYPE_MULTISET:
    case PT_TYPE_SEQUENCE:
    case PT_TYPE_BLOB:
    case PT_TYPE_CLOB:
    case PT_TYPE_OBJECT:
      cast_is_valid = PT_CAST_INVALID;
      break;
    default:
      break;
    }
      break;
    case PT_TYPE_DATE:
      switch (cast_type)
    {
    case PT_TYPE_INTEGER:
    case PT_TYPE_BIGINT:
    case PT_TYPE_FLOAT:
    case PT_TYPE_DOUBLE:
    case PT_TYPE_SMALLINT:
    case PT_TYPE_MONETARY:
    case PT_TYPE_NUMERIC:
    case PT_TYPE_BIT:
    case PT_TYPE_VARBIT:
    case PT_TYPE_TIME:
    case PT_TYPE_SET:
    case PT_TYPE_MULTISET:
    case PT_TYPE_SEQUENCE:
    case PT_TYPE_BLOB:
    case PT_TYPE_CLOB:
    case PT_TYPE_OBJECT:
      cast_is_valid = PT_CAST_INVALID;
      break;
    default:
      break;
    }
      break;
    case PT_TYPE_TIME:
      switch (cast_type)
    {
    case PT_TYPE_INTEGER:
    case PT_TYPE_BIGINT:
    case PT_TYPE_FLOAT:
    case PT_TYPE_DOUBLE:
    case PT_TYPE_SMALLINT:
    case PT_TYPE_MONETARY:
    case PT_TYPE_NUMERIC:
    case PT_TYPE_BIT:
    case PT_TYPE_VARBIT:
    case PT_TYPE_DATE:
    case PT_TYPE_SET:
    case PT_TYPE_MULTISET:
    case PT_TYPE_SEQUENCE:
    case PT_TYPE_BLOB:
    case PT_TYPE_CLOB:
    case PT_TYPE_OBJECT:
      cast_is_valid = PT_CAST_INVALID;
      break;
    case PT_TYPE_TIMESTAMP:
    case PT_TYPE_TIMESTAMPLTZ:
    case PT_TYPE_TIMESTAMPTZ:
    case PT_TYPE_DATETIME:
    case PT_TYPE_DATETIMELTZ:
    case PT_TYPE_DATETIMETZ:
      cast_is_valid = PT_CAST_UNSUPPORTED;
      break;
    default:
      break;
    }
      break;
    case PT_TYPE_TIMESTAMP:
    case PT_TYPE_TIMESTAMPTZ:
    case PT_TYPE_TIMESTAMPLTZ:
      switch (cast_type)
    {
    case PT_TYPE_INTEGER:
    case PT_TYPE_BIGINT:
    case PT_TYPE_FLOAT:
    case PT_TYPE_DOUBLE:
    case PT_TYPE_SMALLINT:
    case PT_TYPE_MONETARY:
    case PT_TYPE_NUMERIC:
    case PT_TYPE_BIT:
    case PT_TYPE_VARBIT:
    case PT_TYPE_SET:
    case PT_TYPE_MULTISET:
    case PT_TYPE_SEQUENCE:
    case PT_TYPE_BLOB:
    case PT_TYPE_CLOB:
    case PT_TYPE_OBJECT:
      cast_is_valid = PT_CAST_INVALID;
      break;
    default:
      break;
    }
      break;
    case PT_TYPE_DATETIME:
    case PT_TYPE_DATETIMETZ:
    case PT_TYPE_DATETIMELTZ:
      switch (cast_type)
    {
    case PT_TYPE_INTEGER:
    case PT_TYPE_FLOAT:
    case PT_TYPE_DOUBLE:
    case PT_TYPE_SMALLINT:
    case PT_TYPE_MONETARY:
    case PT_TYPE_NUMERIC:
    case PT_TYPE_BIT:
    case PT_TYPE_VARBIT:
    case PT_TYPE_SET:
    case PT_TYPE_MULTISET:
    case PT_TYPE_SEQUENCE:
    case PT_TYPE_BLOB:
    case PT_TYPE_CLOB:
    case PT_TYPE_OBJECT:
      cast_is_valid = PT_CAST_INVALID;
      break;
    default:
      break;
    }
      break;
    case PT_TYPE_CHAR:
    case PT_TYPE_VARCHAR:
      switch (cast_type)
    {
    case PT_TYPE_SET:
    case PT_TYPE_MULTISET:
    case PT_TYPE_SEQUENCE:
      cast_is_valid = PT_CAST_UNSUPPORTED;
      break;
    case PT_TYPE_OBJECT:
      cast_is_valid = PT_CAST_INVALID;
      break;
    default:
      break;
    }
      break;
    case PT_TYPE_BIT:
    case PT_TYPE_VARBIT:
      switch (cast_type)
    {
    case PT_TYPE_INTEGER:
    case PT_TYPE_BIGINT:
    case PT_TYPE_FLOAT:
    case PT_TYPE_DOUBLE:
    case PT_TYPE_SMALLINT:
    case PT_TYPE_MONETARY:
    case PT_TYPE_NUMERIC:
    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_DATETIMELTZ:
    case PT_TYPE_DATETIMETZ:
    case PT_TYPE_SET:
    case PT_TYPE_MULTISET:
    case PT_TYPE_SEQUENCE:
    case PT_TYPE_OBJECT:
      cast_is_valid = PT_CAST_INVALID;
      break;
    case PT_TYPE_CLOB:
      cast_is_valid = PT_CAST_UNSUPPORTED;
      break;
    default:
      break;
    }
      break;
    case PT_TYPE_OBJECT:
      /* some functions like DECODE, CASE perform wrap with CAST, allow it */
      if (!PT_EXPR_INFO_IS_FLAGED (node, PT_EXPR_INFO_CAST_SHOULD_FOLD))
    {
      cast_is_valid = PT_CAST_UNSUPPORTED;
    }
      break;
    case PT_TYPE_SET:
    case PT_TYPE_MULTISET:
    case PT_TYPE_SEQUENCE:
      switch (cast_type)
    {
    case PT_TYPE_INTEGER:
    case PT_TYPE_BIGINT:
    case PT_TYPE_FLOAT:
    case PT_TYPE_DOUBLE:
    case PT_TYPE_SMALLINT:
    case PT_TYPE_MONETARY:
    case PT_TYPE_NUMERIC:
    case PT_TYPE_BIT:
    case PT_TYPE_VARBIT:
    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_BLOB:
    case PT_TYPE_CLOB:
    case PT_TYPE_OBJECT:
      cast_is_valid = PT_CAST_INVALID;
      break;
    case PT_TYPE_CHAR:
    case PT_TYPE_VARCHAR:
      cast_is_valid = PT_CAST_UNSUPPORTED;
      break;
    default:
      break;
    }
      break;
    case PT_TYPE_BLOB:
      switch (cast_type)
    {
    case PT_TYPE_BIT:
    case PT_TYPE_VARBIT:
    case PT_TYPE_BLOB:
    case PT_TYPE_ENUMERATION:
      break;
    case PT_TYPE_CLOB:
      cast_is_valid = PT_CAST_UNSUPPORTED;
      break;
    default:
      cast_is_valid = PT_CAST_INVALID;
      break;
    }
      break;
    case PT_TYPE_CLOB:
      switch (cast_type)
    {
    case PT_TYPE_CHAR:
    case PT_TYPE_VARCHAR:
    case PT_TYPE_CLOB:
    case PT_TYPE_ENUMERATION:
      break;
    case PT_TYPE_BLOB:
      cast_is_valid = PT_CAST_UNSUPPORTED;
      break;
    default:
      cast_is_valid = PT_CAST_INVALID;
      break;
    }
      break;
    default:
      break;
    }

  switch (cast_is_valid)
    {
    case PT_CAST_VALID:
      break;
    case PT_CAST_INVALID:
      PT_ERRORmf2 (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CANT_COERCE_TO,
           pt_short_print (parser, node->info.expr.arg1), pt_show_type_enum (cast_type));
      break;
    case PT_CAST_UNSUPPORTED:
      PT_ERRORmf2 (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_COERCE_UNSUPPORTED,
           pt_short_print (parser, node->info.expr.arg1), pt_show_type_enum (cast_type));
      break;
    }

  return (cast_is_valid == PT_CAST_VALID) ? true : false;
}

/*
 * pt_check_user_exists () -  given 'user.class', check that 'user' exists
 *   return:  db_user instance if user exists, NULL otherwise.
 *   parser(in): the parser context used to derive cls_ref
 *   cls_ref(in): a PT_NAME node
 *
 * Note :
 *   this routine is needed only in the context of checking create stmts,
 *   ie, in checking 'create vclass usr.cls ...'.
 *   Otherwise, pt_check_user_owns_class should be used.
 */
static DB_OBJECT *
pt_check_user_exists (PARSER_CONTEXT * parser, PT_NODE * cls_ref)
{
  const char *user_name = NULL;
  DB_OBJECT *result;

  assert (parser != NULL);

  user_name = pt_get_qualifier_name (parser, cls_ref);
  if (user_name == NULL || user_name[0] == '\0')
    {
      return NULL;
    }

  result = db_find_user (user_name);
  if (!result)
    {
      PT_ERRORmf (parser, cls_ref, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_USER_IS_NOT_IN_DB, user_name);
    }

  return result;
}

/*
 * pt_check_user_owns_class () - given user.class, check that user owns class
 *   return:  db_user instance if 'user' exists & owns 'class', NULL otherwise
 *   parser(in): the parser context used to derive cls_ref
 *   cls_ref(in): a PT_NAME node
 */
DB_OBJECT *
pt_check_user_owns_class (PARSER_CONTEXT * parser, PT_NODE * cls_ref)
{
  DB_OBJECT *result, *cls, *owner;

  if ((result = pt_check_user_exists (parser, cls_ref)) == NULL || (cls = cls_ref->info.name.db_object) == NULL)
    {
      return NULL;
    }

  /* This is the case when the loaddb utility is executed with the --no-user-specified-name option as the dba user. */
  if (db_get_client_type () == DB_CLIENT_TYPE_ADMIN_LOADDB_COMPAT_UNDER_11_2)
    {
      return result;
    }

  owner = db_get_owner (cls);
  result = (ws_is_same_object (owner, result) ? result : NULL);
  if (!result)
    {
      PT_ERRORmf2 (parser, cls_ref, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_USER_DOESNT_OWN_CLS,
           cls_ref->info.name.resolved, cls_ref->info.name.original);
    }

  return result;
}

/*
 * pt_derive_attribute () - derive a new ATTR_DEF node from a query_spec
 *                          column
 *   return:  a new ATTR_DEF node derived from c if all OK, NULL otherwise.
 *   parser(in): the parser context to use for creating the ATTR_DEF node
 *   c(in): a query_spec column
 */
static PT_NODE *
pt_derive_attribute (PARSER_CONTEXT * parser, PT_NODE * c)
{
  PT_NODE *attr = NULL;
  PT_NODE *cname = NULL;

  assert (parser != NULL);

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

  if (c->alias_print != NULL)
    {
      cname = pt_name (parser, c->alias_print);
    }
  else if (c->node_type == PT_NAME)
    {
      if (c->type_enum == PT_TYPE_OBJECT && c->info.name.meta_class == PT_OID_ATTR
      && (c->info.name.original == NULL || strlen (c->info.name.original) == 0))
    {
      cname = pt_name (parser, c->info.name.resolved);
    }
      else
    {
      cname = pt_name (parser, c->info.name.original);
    }
    }
  else
    {
      return NULL;
    }

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

  attr = parser_new_node (parser, PT_ATTR_DEF);
  if (attr == NULL)
    {
      return NULL;
    }

  attr->data_type = NULL;
  attr->info.attr_def.attr_name = cname;
  attr->info.attr_def.attr_type = PT_NORMAL;

  return attr;
}

/*
 * pt_get_attributes () - get & return the attribute list of
 *                        a {class|vclass|view}
 *   return:  c's attribute list if successful, NULL otherwise.
 *   parser(in): the parser context to use for creating the list
 *   c(in): a {class|vclass|view} object
 */
/* TODO modify the function so that we can distinguish between a class having
 *      no attributes and an execution error.
 */
static PT_NODE *
pt_get_attributes (PARSER_CONTEXT * parser, const DB_OBJECT * c)
{
  DB_ATTRIBUTE *attributes;
  const char *class_name;
  PT_NODE *i_attr, *name, *typ, *types, *list = NULL;
  DB_OBJECT *cls;
  DB_DOMAIN *dom;

  assert (parser != NULL);

  if (!c || !(class_name = db_get_class_name ((DB_OBJECT *) c)))
    {
      return list;
    }

  attributes = db_get_attributes ((DB_OBJECT *) c);
  while (attributes)
    {
      /* create a new attribute node */
      i_attr = parser_new_node (parser, PT_ATTR_DEF);
      if (i_attr == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

      /* its name is class_name.attribute_name */
      i_attr->info.attr_def.attr_name = name = pt_name (parser, db_attribute_name (attributes));
      name->info.name.resolved = pt_append_string (parser, NULL, class_name);
      PT_NAME_INFO_SET_FLAG (name, PT_NAME_DEFAULTF_ACCEPTS);
      name->info.name.meta_class = (db_attribute_is_shared (attributes) ? PT_SHARED : PT_NORMAL);

      /* set its data type */
      i_attr->type_enum = pt_db_to_type_enum (db_attribute_type (attributes));
      switch (i_attr->type_enum)
    {
    case PT_TYPE_OBJECT:
      cls = db_domain_class (db_attribute_domain (attributes));
      if (cls)
        {
          name = pt_name (parser, db_get_class_name (cls));
          name->info.name.meta_class = PT_CLASS;
          name->info.name.db_object = cls;
          name->info.name.spec_id = (UINTPTR) name;
          i_attr->data_type = typ = parser_new_node (parser, PT_DATA_TYPE);
          if (typ)
        {
          typ->info.data_type.entity = name;
        }
        }
      break;

    case PT_TYPE_SET:
    case PT_TYPE_SEQUENCE:
    case PT_TYPE_MULTISET:
      types = NULL;
      dom = db_domain_set (db_attribute_domain (attributes));
      while (dom)
        {
          typ = pt_domain_to_data_type (parser, dom);
          if (typ)
        {
          typ->next = types;
        }
          types = typ;
          dom = db_domain_next (dom);
        }
      i_attr->data_type = types;
      break;

    default:
      dom = attributes->domain;
      typ = pt_domain_to_data_type (parser, dom);
      i_attr->data_type = typ;
      break;
    }

      list = parser_append_node (i_attr, list);

      /* advance to next attribute */
      attributes = db_attribute_next (attributes);
    }
  return list;
}

/*
 * pt_get_class_type () - return a class instance's type
 *   return:  PT_CLASS, PT_VCLASS, or PT_MISC_DUMMY
 *   cls(in): a class instance
 */
static PT_MISC_TYPE
pt_get_class_type (PARSER_CONTEXT * parser, const DB_OBJECT * cls)
{
  if (!cls)
    {
      return PT_MISC_DUMMY;
    }

  if (db_is_vclass ((DB_OBJECT *) cls) > 0)
    {
      return PT_VCLASS;
    }

  if (db_is_class ((DB_OBJECT *) cls) > 0)
    {
      return PT_CLASS;
    }

  return PT_MISC_DUMMY;
}

/*
 * pt_number_of_attributes () - determine the number of attributes
 *      of the new class to be created by a create_vclass statement,
 *  or the number of attributes of the new definition of a view
 *  in the case of "ALTER VIEW xxx AS SELECT ...".
 *   return:  number of attributes of the new class to be created by stmt
 *   parser(in): the parser context used to derive stmt
 *   stmt(in): a create_vclass statement
 *   attrs(out): the attributes of the new class to be created by stmt
 *
 * Note :
 * non-inherited class_attributes are excluded from the attribute count.
 */
static int
pt_number_of_attributes (PARSER_CONTEXT * parser, PT_NODE * stmt, PT_NODE ** attrs)
{
  int count = 0;
  PT_NODE *crt_attr = NULL;
  PT_NODE *crt_parent = NULL;
  PT_NODE *t_attr = NULL;
  PT_NODE *inherited_attrs = NULL;
  PT_NODE *r = NULL;
  PT_NODE *i_attr = NULL;
  PT_NODE *next_node = NULL;

  if (stmt == NULL || attrs == NULL)
    {
      return count;
    }

  if ((stmt->node_type == PT_ALTER) && (stmt->info.alter.code == PT_RESET_QUERY))
    {
      *attrs = stmt->info.alter.alter_clause.query.attr_def_list;
      count = pt_length_of_list (*attrs);
      return count;
    }

  assert (stmt->node_type == PT_CREATE_ENTITY);
  if (stmt->node_type != PT_CREATE_ENTITY)
    {
      return count;
    }

  *attrs = stmt->info.create_entity.attr_def_list;
  count = pt_length_of_list (*attrs);

  /* Exclude class_attributes from count but keep them in the attrs list. */
  for (crt_attr = *attrs; crt_attr != NULL; crt_attr = crt_attr->next)
    {
      if (crt_attr->info.attr_def.attr_type == PT_META_ATTR)
    {
      count--;
    }
    }

  /* collect into one list all inherited attributes from all parents */
  inherited_attrs = NULL;
  for (crt_parent = stmt->info.create_entity.supclass_list; crt_parent != NULL; crt_parent = crt_parent->next)
    {
      /* get this parent's attributes & append them to the list */
      PT_NODE *const parent_attrs = pt_get_attributes (parser, crt_parent->info.name.db_object);

      inherited_attrs = parser_append_node (parent_attrs, inherited_attrs);
    }

  /* Rule 2: If two or more superclasses have attributes with the same name and domain but different origins, the class
   * may inherit one or more of the attributes, but the user needs to specify inheritance. Implementation: scan through
   * the inheritance list and do any attribute renaming specified by the user. */
  for (r = stmt->info.create_entity.resolution_list; r != NULL; r = r->next)
    {
      PT_NODE *const new_name = r->info.resolution.as_attr_mthd_name;
      PT_NODE *const resolv_class = r->info.resolution.of_sup_class_name;
      PT_NODE *const resolv_attr = r->info.resolution.attr_mthd_name;

      if (new_name == NULL)
    {
      continue;
    }

      for (i_attr = inherited_attrs; i_attr != NULL; t_attr = i_attr, i_attr = i_attr->next)
    {
      PT_NODE *const name = i_attr->info.attr_def.attr_name;

      if (pt_str_compare (resolv_attr->info.name.original, name->info.name.original, CASE_INSENSITIVE) == 0
          && pt_user_specified_name_compare (resolv_class->info.name.original, name->info.name.resolved) == 0)
        {
          name->info.name.original = new_name->info.name.original;
        }
    }
    }

  /* Rule 2 implementation continued: remove from inherited_attrs all inherited attributes that conflict with any
   * user-specified inheritance. */
  for (r = stmt->info.create_entity.resolution_list; r != NULL; r = r->next)
    {
      PT_NODE *const resolv_class = r->info.resolution.of_sup_class_name;
      PT_NODE *const resolv_attr = r->info.resolution.attr_mthd_name;

      if (r->info.resolution.as_attr_mthd_name != NULL)
    {
      continue;
    }

      /* user wants class to inherit this attribute without renaming */
      for (i_attr = inherited_attrs; i_attr != NULL; i_attr = i_attr->next)
    {
      PT_NODE *const name = i_attr->info.attr_def.attr_name;

      if (pt_str_compare (resolv_attr->info.name.original, name->info.name.original, CASE_INSENSITIVE) != 0)
        {
          /* i_attr is a keeper so advance t_attr. */
          t_attr = i_attr;
        }
      else
        {
          if (pt_user_specified_name_compare (resolv_class->info.name.original, name->info.name.resolved) == 0)
        {
          /* i_attr is a keeper. keep the user-specified inherited attribute */
          t_attr = i_attr;
          continue;
        }
          /* delete inherited attribute that conflicts with resolv_attr */
          if (i_attr == inherited_attrs)
        {
          inherited_attrs = i_attr->next;
        }
          else
        {
          t_attr->next = i_attr->next;
        }
          /* i_attr is a goner. do NOT advance t_attr! */
        }
    }
    }

  /*
   * At this point, the conflicting attributes that the user wants us to keep have been safely preserved and renamed in
   * inherited_attrs. It is now safe to start weeding out remaining attribute conflicts. */

  /*
   * Rule 1: If the name of an attribute in a class C conflicts (i.e., is the same as) with that of an attribute in a
   * superclass S, the name in class C is used; that is, the attribute is not inherited. Implementation: remove from
   * inherited_attrs each attribute whose name matches some non-inherited attribute name. */
  for (crt_attr = stmt->info.create_entity.attr_def_list; crt_attr != NULL; crt_attr = crt_attr->next)
    {
      for (i_attr = inherited_attrs; i_attr; i_attr = i_attr->next)
    {
      if (pt_str_compare (crt_attr->info.attr_def.attr_name->info.name.original,
                  i_attr->info.attr_def.attr_name->info.name.original, CASE_INSENSITIVE) != 0)
        {
          /* i_attr is a keeper. */
          t_attr = i_attr;
        }
      else
        {
          /* delete it from inherited_attrs */
          if (i_attr == inherited_attrs)
        {
          inherited_attrs = i_attr->next;
        }
          else
        {
          t_attr->next = i_attr->next;
        }
          /* i_attr is a goner. do NOT advance t_attr! */
        }
    }
    }

  /*
   * Rule 2 continued: If the user does not specify the attributes (to be inherited), the system will pick one
   * arbitrarily, and notify the user. Jeff probably knows how to 'pick one arbitrarily', but until we learn how, the
   * following will do for TPR.  We lump together Rules 2 & 3 and implement them as: given a group of attributes with
   * the same name, keep the first and toss the rest. */
  for (i_attr = inherited_attrs, next_node = i_attr ? i_attr->next : NULL; i_attr != NULL;
       i_attr = next_node, next_node = i_attr ? i_attr->next : NULL)
    {
      for (r = i_attr->next; r != NULL; r = r->next)
    {
      if (pt_str_compare (i_attr->info.attr_def.attr_name->info.name.original,
                  r->info.attr_def.attr_name->info.name.original, CASE_INSENSITIVE) != 0)
        {
          /* r is a keeper so advance t_attr. */
          t_attr = r;
        }
      else
        {
          if (r == i_attr->next)
        {
          i_attr->next = r->next;
        }
          else
        {
          t_attr->next = r->next;
        }
          /* r is a goner. do NOT advance t_attr! */
        }
    }
    }

  /* Append the non-inherited attributes to the inherited attributes. */
  if (inherited_attrs != NULL)
    {
      count += pt_length_of_list (inherited_attrs);
      *attrs = parser_append_node (*attrs, inherited_attrs);
    }

  return count;
}

/*
 * pt_is_real_class_of_vclass () - determine if s_class is a
 *                                 real class of d_class
 *   return:  1 if s_class is a real class of the view d_class
 *   parser(in): the parser context
 *   s_class(in): a PT_DATA_TYPE node whose type_enum is PT_TYPE_OBJECT
 *   d_class(in): a PT_DATA_TYPE node whose type_enum is PT_TYPE_OBJECT
 */
static int
pt_is_real_class_of_vclass (PARSER_CONTEXT * parser, const PT_NODE * s_class, const PT_NODE * d_class)
{
  if (!d_class || d_class->node_type != PT_DATA_TYPE || !s_class || s_class->node_type != PT_DATA_TYPE)
    {
      return 0;
    }

  return mq_is_real_class_of_vclass (parser, s_class->info.data_type.entity, d_class->info.data_type.entity);
}

/*
 * pt_objects_assignable () - determine if src is assignable to data_type dest
 *   return:  1 iff src is assignable to dest, 0 otherwise
 *   parser(in): the parser context
 *   d_class_dt(in): data_type of target attribute
 *   s_class(in): source PT_NODE
 */
static int
pt_objects_assignable (PARSER_CONTEXT * parser, const PT_NODE * d_class_dt, const PT_NODE * s_class)
{
  PT_NODE *s_class_type, *d_class_dt_type = NULL;

  if (!s_class || s_class->type_enum != PT_TYPE_OBJECT)
    {
      return 0;
    }

  if (!d_class_dt || (d_class_dt->node_type == PT_DATA_TYPE && !(d_class_dt_type = d_class_dt->info.data_type.entity)))
    {
      /* a wildcard destination object matches any other object type */
      return 1;
    }

  else if ((d_class_dt && d_class_dt->node_type != PT_DATA_TYPE) || !s_class->data_type
       || s_class->data_type->node_type != PT_DATA_TYPE)
    {
      /* weed out structural errors as failures */
      return 0;
    }
  else
    {
      /* s_class is assignable to d_class_dt if s_class is a subclass of d_class_dt this is what it should be: return
       * pt_is_subset_of(parser, s_class_type, d_class_dt_type); but d_class_dt->info.data_type.entity does not have
       * ALL the subclasses of the type, ie, if d_class_dt's type is "glo", it shows only "glo" instead of: "glo,
       * audio, etc." so we do this instead: */
      if (!(s_class_type = s_class->data_type->info.data_type.entity))
    {
      return 1;     /* general object type */
    }
      else
    {
      return ((s_class_type->info.name.db_object == d_class_dt_type->info.name.db_object)
          || (db_is_subclass (s_class_type->info.name.db_object, d_class_dt_type->info.name.db_object) > 0));
    }
    }
}

/*
 * pt_class_assignable () - determine if s_class is assignable to d_class_dt
 *   return:  1 if s_class is assignable to d_class
 *   parser(in): the parser context
 *   d_class_dt(in): a PT_DATA_TYPE node whose type_enum is PT_TYPE_OBJECT
 *   s_class(in): a PT_NODE whose type_enum is PT_TYPE_OBJECT
 */
int
pt_class_assignable (PARSER_CONTEXT * parser, const PT_NODE * d_class_dt, const PT_NODE * s_class)
{

  /* a wildcard destination object accepts any other object type */
  if (!d_class_dt || (d_class_dt->node_type == PT_DATA_TYPE && !d_class_dt->info.data_type.entity))
    {
      return 1;
    }

  /* weed out structural errors as failures */
  if (!s_class || (d_class_dt && d_class_dt->node_type != PT_DATA_TYPE))
    {
      return 0;
    }

  /* NULL is assignable to any class type */
  if (s_class->type_enum == PT_TYPE_NA || s_class->type_enum == PT_TYPE_NULL)
    {
      return 1;
    }

  /* make sure we are dealing only with object types */
  if (s_class->type_enum != PT_TYPE_OBJECT)
    {
      return 0;
    }

  return (pt_objects_assignable (parser, d_class_dt, s_class)
      || pt_is_real_class_of_vclass (parser, s_class->data_type, d_class_dt)
      || pt_is_real_class_of_vclass (parser, d_class_dt, s_class->data_type));
}

/*
 * pt_class_compatible () - determine if two classes have compatible domains
 *   return:  1 if class1 and class2 have compatible domains
 *   parser(in): the parser context
 *   class1(in): a PT_NODE whose type_enum is PT_TYPE_OBJECT
 *   class2(in): a PT_NODE whose type_enum is PT_TYPE_OBJECT
 *   view_definition_context(in):
 */
static int
pt_class_compatible (PARSER_CONTEXT * parser, const PT_NODE * class1, const PT_NODE * class2,
             bool view_definition_context)
{
  if (!class1 || class1->type_enum != PT_TYPE_OBJECT || !class2 || class2->type_enum != PT_TYPE_OBJECT)
    {
      return 0;
    }

  if (view_definition_context)
    {
      return pt_class_assignable (parser, class1->data_type, class2);
    }
  else
    {
      return (pt_class_assignable (parser, class1->data_type, class2)
          || pt_class_assignable (parser, class2->data_type, class1));
    }
}

/*
 * pt_vclass_compatible () - determine if att is vclass compatible with qcol
 *   return:  true if att is vclass compatible with qcol
 *   parser(in): the parser context
 *   att(in): PT_DATA_TYPE node of a vclass attribute def
 *   qcol(in): a query spec column
 */
static bool
pt_vclass_compatible (PARSER_CONTEXT * parser, const PT_NODE * att, const PT_NODE * qcol)
{
  PT_NODE *entity, *qcol_entity, *qcol_typ;
  DB_OBJECT *vcls = NULL;
  const char *clsnam = NULL, *qcol_typnam = NULL, *spec, *qs_clsnam;
  DB_QUERY_SPEC *specs;

  /* a wildcard object accepts any other object type but is not vclass_compatible with any other object */
  if (!att || (att->node_type == PT_DATA_TYPE && !att->info.data_type.entity))
    {
      return false;
    }

  /* weed out structural errors as failures */
  if (!qcol || (att && att->node_type != PT_DATA_TYPE) || (entity = att->info.data_type.entity) == NULL
      || entity->node_type != PT_NAME || ((vcls = entity->info.name.db_object) == NULL
                      && (clsnam = entity->info.name.original) == NULL))
    {
      return false;
    }

  /* make sure we are dealing only with object types that can be union vclass_compatible with vcls. */
  if (qcol->type_enum != PT_TYPE_OBJECT || (qcol_typ = qcol->data_type) == NULL || qcol_typ->node_type != PT_DATA_TYPE
      || (qcol_entity = qcol_typ->info.data_type.entity) == NULL || qcol_entity->node_type != PT_NAME
      || (qcol_typnam = qcol_entity->info.name.original) == NULL)
    {
      return false;
    }

  /* make sure we have the vclass */
  if (!vcls)
    {
      vcls = db_find_class (clsnam);
    }
  if (!vcls)
    {
      return false;
    }

  /* return true iff att is a vclass & qcol is in att's query_spec list */
  bool bret = false;
  PARSER_CONTEXT *tmp_parser = parser_create_parser ();
  if (tmp_parser == NULL)
    {
      return false;
    }

  for (specs = db_get_query_specs (vcls); specs && (spec = db_query_spec_string (specs));
       specs = db_query_spec_next (specs))
    {
      qs_clsnam = pt_get_proxy_spec_name (tmp_parser, spec);
      if (qs_clsnam && intl_identifier_casecmp (qs_clsnam, qcol_typnam) == 0)
    {
      bret = true;      /* att is vclass_compatible with qcol */
      break;
    }
    }

  parser_free_parser (tmp_parser);
  return bret;          /* att is not vclass_compatible with qcol */
}

/*
 * pt_type_assignable () - determine if s_type is assignable to d_type
 *   return:  1 if s_type is assignable to d_type
 *   parser(in): the parser context
 *   d_type(in): a PT_DATA_TYPE node whose type_enum is PT_TYPE_OBJECT
 *   s_type(in): a PT_DATA_TYPE node whose type_enum is PT_TYPE_OBJECT
 */
static int
pt_type_assignable (PARSER_CONTEXT * parser, const PT_NODE * d_type, const PT_NODE * s_type)
{
  PT_NODE *src_type, *dest_type = NULL;

  /* a wildcard destination object accepts any other object type */
  if (!d_type || (d_type->node_type == PT_DATA_TYPE && !d_type->info.data_type.entity))
    {
      return 1;
    }

  /* weed out structural errors as failures */
  if (!s_type || (d_type && d_type->node_type != PT_DATA_TYPE))
    {
      return 0;
    }

  /* make sure we are dealing only with object types */
  if (s_type->type_enum != PT_TYPE_OBJECT)
    {
      return 0;
    }

  dest_type = d_type->info.data_type.entity;
  src_type = s_type->info.data_type.entity;
  if (!dest_type || !src_type)
    {
      return 0;
    }

  /* If the destination isn't resolved, resolve it. */
  if (!dest_type->info.name.db_object)
    {
      dest_type->info.name.db_object = db_find_class (dest_type->info.name.original);
      dest_type->info.name.meta_class = PT_CLASS;
    }

  return (src_type->info.name.db_object == dest_type->info.name.db_object
      || (db_is_subclass (src_type->info.name.db_object, dest_type->info.name.db_object) > 0)
      || mq_is_real_class_of_vclass (parser, src_type, dest_type));
}

/*
 * pt_collection_assignable () - determine if s_col is assignable to d_col
 *   return:  1 if s_col is assignable to d_col
 *   parser(in): the parser context
 *   d_col(in): a PT_NODE whose type_enum is a PT_IS_COLLECTION_TYPE
 *   s_col(in): a PT_NODE whose type_enum is a PT_IS_COLLECTION_TYPE
 */
static int
pt_collection_assignable (PARSER_CONTEXT * parser, const PT_NODE * d_col, const PT_NODE * s_col)
{
  int assignable = 1;       /* innocent until proven guilty */

  if (!d_col || !s_col || !PT_IS_COLLECTION_TYPE (d_col->type_enum))
    {
      return 0;
    }

  /* NULL is assignable to any class type */
  if (s_col->type_enum == PT_TYPE_NA || s_col->type_enum == PT_TYPE_NULL)
    {
      return 1;
    }

  /* make sure we are dealing only with collection types */
  if (!PT_IS_COLLECTION_TYPE (s_col->type_enum))
    {
      return 0;
    }

  /* can't assign a multiset or a sequence to a set, or a multiset to a sequence */
  if (((d_col->type_enum == PT_TYPE_SET)
       && ((s_col->type_enum == PT_TYPE_MULTISET) || (s_col->type_enum == PT_TYPE_SEQUENCE)))
      || ((d_col->type_enum == PT_TYPE_SEQUENCE) && (s_col->type_enum == PT_TYPE_MULTISET)))
    {
      assignable = 0;
    }
  else if (!d_col->data_type)
    {
      /* the wildcard set (set of anything) can be assigned a set of any type. */
      assignable = 1;
    }
  else if (!s_col->data_type)
    {
      /* in this case, we have a wild card set being assigned to a non-wildcard set. */
      assignable = 0;
    }
  else
    {
      /* Check to see that every type in the source collection is in the destination collection.  That is, the source
       * types must be a subset of the destination types.  There is no coercion allowed. */
      PT_NODE *st, *dt;
      int found;

      for (st = s_col->data_type; st != NULL; st = st->next)
    {
      found = 0;
      for (dt = d_col->data_type; dt != NULL; dt = dt->next)
        {
          if (st->type_enum == dt->type_enum)
        {
          if ((st->type_enum != PT_TYPE_OBJECT) || (pt_type_assignable (parser, dt, st)))
            {
              found = 1;
              break;
            }
        }
        }

      if (!found)
        {
          assignable = 0;
          break;
        }
    }
    }

  return assignable;
}               /* pt_collection_assignable */

/*
 * pt_collection_compatible () - determine if two collections
 *                               have compatible domains
 *   return:  1 if c1 and c2 have compatible domains
 *   parser(in): the parser context
 *   col1(in): a PT_NODE whose type_enum is PT_TYPE_OBJECT
 *   col2(in): a PT_NODE whose type_enum is PT_TYPE_OBJECT
 *   view_definition_context(in):
 */
static int
pt_collection_compatible (PARSER_CONTEXT * parser, const PT_NODE * col1, const PT_NODE * col2,
              bool view_definition_context)
{
  if (!col1 || !PT_IS_COLLECTION_TYPE (col1->type_enum) || !col2 || !PT_IS_COLLECTION_TYPE (col2->type_enum))
    {
      return 0;
    }

  if (view_definition_context)
    {
      return pt_collection_assignable (parser, col1, col2);
    }
  else
    {
      return (col1->type_enum == col2->type_enum
          && (pt_collection_assignable (parser, col1, col2) || pt_collection_assignable (parser, col2, col1)));
    }
}

/*
 * pt_union_compatible () - determine if two select_list items are
 *                          union compatible
 *   return:  1 if item1 and item2 are union compatible.
 *   parser(in): the parser context
 *   item1(in): an element of a select_list or attribute_list
 *   item2(in): an element of a select_list or attribute_list
 *   view_definition_context(in):
 *   is_object_type(in):
 *
 * Note :
 *   return 1 if:
 *   - item1 or  item2 is "NA", or
 *   - item1 and item2 have identical types, or
 *   - item1 is a literal that can be coerced to item2's type, or
 *   - item2 is a literal that can be coerced to item1's type.
 */

static PT_UNION_COMPATIBLE
pt_union_compatible (PARSER_CONTEXT * parser, PT_NODE * item1, PT_NODE * item2, bool view_definition_context,
             bool * is_object_type)
{
  PT_TYPE_ENUM typ1, typ2, common_type;
  PT_NODE *dt1, *dt2, *data_type;
  int r;

  typ1 = item1->type_enum;
  typ2 = item2->type_enum;
  *is_object_type = false;

  if (typ1 == typ2 && typ1 != PT_TYPE_OBJECT && !PT_IS_COLLECTION_TYPE (typ1))
    {
      if (typ1 == PT_TYPE_NONE) /* is not compatible with anything */
    {
      return PT_UNION_INCOMP_CANNOT_FIX;
    }

      if (typ1 == PT_TYPE_MAYBE)
    {
      /* assume hostvars are compatible */
      return PT_UNION_COMP;
    }

      if (!view_definition_context)
    {
      dt1 = item1->data_type;
      dt2 = item2->data_type;
      common_type = typ1;

      if (dt1 && dt2)
        {
          /* numeric type, fixed size string type */
          if (common_type == PT_TYPE_NUMERIC || PT_IS_STRING_TYPE (common_type))
        {
          if ((dt1->info.data_type.precision != dt2->info.data_type.precision)
              || (dt1->info.data_type.dec_precision != dt2->info.data_type.dec_precision)
              || (dt1->info.data_type.units != dt2->info.data_type.units))
            {
              /* different numeric and fixed size string types are incompatible */
              return PT_UNION_INCOMP;
            }
        }
        }
      else
        {
          return PT_UNION_INCOMP;
        }
    }
      return PT_UNION_COMP;
    }

  if (typ2 == PT_TYPE_NA || typ2 == PT_TYPE_NULL)
    {
      /* NA is compatible with any type except PT_TYPE_NONE */
      return ((typ1 != PT_TYPE_NONE) ? PT_UNION_COMP : PT_UNION_INCOMP_CANNOT_FIX);
    }

  if (typ1 == PT_TYPE_NA || typ1 == PT_TYPE_NULL)
    {
      /* NA is compatible with any type except PT_TYPE_NONE */
      return ((typ2 != PT_TYPE_NONE) ? PT_UNION_COMP : PT_UNION_INCOMP_CANNOT_FIX);
    }

  if (view_definition_context)
    {
      common_type = typ1;
    }
  else
    {
      common_type = pt_common_type (typ1, typ2);
    }

  if (common_type == PT_TYPE_NONE)  /* not union compatible */
    {
      return PT_UNION_INCOMP_CANNOT_FIX;
    }

  if (item1->node_type == PT_VALUE || item2->node_type == PT_VALUE)
    {
      data_type = NULL;
      if (common_type == PT_TYPE_NUMERIC)
    {
      SEMAN_COMPATIBLE_INFO ci1, ci2;

      pt_get_compatible_info_from_node (item1, &ci1);
      pt_get_compatible_info_from_node (item2, &ci2);

      data_type = parser_new_node (parser, PT_DATA_TYPE);
      if (data_type == NULL)
        {
          return PT_UNION_ERROR;
        }
      data_type->info.data_type.precision =
        MAX ((ci1.prec - ci1.scale), (ci2.prec - ci2.scale)) + MAX (ci1.scale, ci2.scale);
      data_type->info.data_type.dec_precision = MAX (ci1.scale, ci2.scale);

      if (data_type->info.data_type.precision > DB_MAX_NUMERIC_PRECISION)
        {
          data_type->info.data_type.dec_precision =
        (DB_MAX_NUMERIC_PRECISION - data_type->info.data_type.dec_precision);
          if (data_type->info.data_type.dec_precision < 0)
        {
          data_type->info.data_type.dec_precision = 0;
        }
          data_type->info.data_type.precision = DB_MAX_NUMERIC_PRECISION;
        }
    }

      if (item1->type_enum == common_type && item2->type_enum == common_type)
    {
      return PT_UNION_COMP;
    }
      else
    {
      return PT_UNION_INCOMP;
    }
    }
  else if (common_type == PT_TYPE_OBJECT)
    {
      *is_object_type = true;
      if ((item1->node_type == PT_NAME && item1->info.name.meta_class == PT_VID_ATTR)
      || (item2->node_type == PT_NAME && item2->info.name.meta_class == PT_VID_ATTR))
    {
      /* system-added OID */
      return PT_UNION_COMP;
    }
      else
    {
      r = pt_class_compatible (parser, item1, item2, view_definition_context);
      return ((r == 1) ? PT_UNION_COMP : PT_UNION_INCOMP_CANNOT_FIX);
    }
    }
  else if (PT_IS_COLLECTION_TYPE (common_type))
    {
      r = pt_collection_compatible (parser, item1, item2, view_definition_context);
      return ((r == 1) ? PT_UNION_COMP : PT_UNION_INCOMP_CANNOT_FIX);
    }

  return PT_UNION_INCOMP;   /* not union compatible */
}

/*
 * pt_is_compatible_without_cast () -
 *   return: true/false
 *   parser(in):
 *   dest_type_enum(in):
 *   dest_prec(in):
 *   dest_scale(in):
 *   src(in):
 */
static bool
pt_is_compatible_without_cast (PARSER_CONTEXT * parser, SEMAN_COMPATIBLE_INFO * dest_sci, PT_NODE * src,
                   bool * is_cast_allowed)
{
  assert (dest_sci != NULL);
  assert (src != NULL);
  assert (is_cast_allowed != NULL);

  *is_cast_allowed = true;

  if (dest_sci->force_cast && dest_sci->type_enum == PT_TYPE_JSON)
    {
      return false;
    }

  if (dest_sci->type_enum != src->type_enum)
    {
      return false;
    }

  if (PT_HAS_COLLATION (src->type_enum))
    {
      if (src->data_type != NULL && src->data_type->info.data_type.collation_id != dest_sci->coll_infer.coll_id)
    {
      INTL_CODESET att_cs;

      att_cs = (INTL_CODESET) src->data_type->info.data_type.units;

      if (!INTL_CAN_COERCE_CS (att_cs, dest_sci->coll_infer.codeset))
        {
          *is_cast_allowed = false;
        }

      return false;
    }
    }

  if (PT_IS_STRING_TYPE (dest_sci->type_enum))
    {
      assert_release (dest_sci->prec != 0);
      if (src->data_type && dest_sci->prec == src->data_type->info.data_type.precision)
    {
      return true;
    }
      else
    {
      return false;
    }
    }
  else if (dest_sci->type_enum == PT_TYPE_NUMERIC)
    {
      assert_release (dest_sci->prec != 0);
      if (src->data_type && dest_sci->prec == src->data_type->info.data_type.precision
      && dest_sci->scale == src->data_type->info.data_type.dec_precision)
    {
      return true;
    }
      else
    {
      return false;
    }
    }
  else if (dest_sci->type_enum == PT_TYPE_ENUMERATION)
    {
      /* enumerations might not have the same domain */
      return false;
    }
  else if (PT_IS_COLLECTION_TYPE (dest_sci->type_enum))
    {
      /* collections might not have the same domain */
      return false;
    }

  return true;          /* is compatible, no need to cast */
}

/*
 * pt_to_compatible_cast () -
 *   return:
 *   parser(in):
 *   node(in):
 *   cinfo(in):
 */
static PT_NODE *
pt_to_compatible_cast (PARSER_CONTEXT * parser, PT_NODE * node, SEMAN_COMPATIBLE_INFO * cinfo, int num_cinfo)
{
  PT_NODE *attrs, *att;
  PT_NODE *prev_att, *next_att, *new_att = NULL, *new_dt = NULL;
  int i;
  bool new_cast_added;
  bool need_to_cast;

  assert (parser != NULL);

  if (!node || !pt_is_query (node))
    {
      return NULL;
    }

  if (pt_is_select (node))
    {
      if (PT_IS_VALUE_QUERY (node))
    {
      node = pt_values_query_to_compatible_cast (parser, node, cinfo, num_cinfo);
    }
      else
    {
      attrs = pt_get_select_list (parser, node);
      if (attrs == NULL)
        {
          return NULL;
        }

      prev_att = NULL;
      for (att = attrs, i = 0; i < num_cinfo && att; att = next_att, i++)
        {
          bool is_cast_allowed = true;

          new_cast_added = false;

          next_att = att->next; /* save next link */

          need_to_cast = false;
          /* find incompatible attr */
          if (cinfo[i].idx == i)
        {
          if (!pt_is_compatible_without_cast (parser, &(cinfo[i]), att, &is_cast_allowed))
            {
              need_to_cast = true;

              /* assertion check */
              if (need_to_cast)
            {
              if (PT_IS_STRING_TYPE (att->type_enum) || att->type_enum == PT_TYPE_NUMERIC)
                {
                  if (att->data_type == NULL)
                {
                  assert_release (att->data_type != NULL);
                  return NULL;
                }
                }
            }
            }
        }

          if (need_to_cast)
        {
          SEMAN_COMPATIBLE_INFO att_cinfo;

          if (!is_cast_allowed)
            {
              return NULL;
            }

          memcpy (&att_cinfo, &(cinfo[i]), sizeof (att_cinfo));

          if (PT_HAS_COLLATION (att->type_enum) && att->data_type != NULL)
            {
              /* use collation and codeset of original attribute the values from cinfo are not usable */
              att_cinfo.coll_infer.coll_id = att->data_type->info.data_type.collation_id;
              att_cinfo.coll_infer.codeset = (INTL_CODESET) att->data_type->info.data_type.units;
            }

          new_att = pt_make_cast_with_compatible_info (parser, att, next_att, &att_cinfo, &new_cast_added);
          if (new_att == NULL)
            {
              goto out_of_mem;
            }

          if (new_cast_added)
            {
              att = new_att;
            }

          if (prev_att == NULL)
            {
              node->info.query.q.select.list = att;
              node->type_enum = att->type_enum;
              if (node->data_type)
            {
              parser_free_tree (parser, node->data_type);
            }
              node->data_type = parser_copy_tree_list (parser, att->data_type);
            }
          else
            {
              prev_att->next = att;
            }
        }

          prev_att = att;
        }
    }
    }
  else
    {               /* PT_UNION, PT_DIFFERENCE, PT_INTERSECTION */
      if (!pt_to_compatible_cast (parser, node->info.query.q.union_.arg1, cinfo, num_cinfo)
      || !pt_to_compatible_cast (parser, node->info.query.q.union_.arg2, cinfo, num_cinfo))
    {
      return NULL;
    }

      if (node->data_type)
    {
      parser_free_tree (parser, node->data_type);
    }
      node->data_type = parser_copy_tree_list (parser, node->info.query.q.union_.arg1->data_type);
    }

  return node;

out_of_mem:
  if (new_att)
    {
      parser_free_tree (parser, new_att);
    }

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

  PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
  return NULL;
}

/*
 * pt_get_compatible_info_from_node () -
 *   return:
 *   att(in):
 *   cinfo(out):
 */
static void
pt_get_compatible_info_from_node (const PT_NODE * att, SEMAN_COMPATIBLE_INFO * cinfo)
{
  assert (cinfo != NULL);

  cinfo->coll_infer.coll_id = -1;
  cinfo->coll_infer.codeset = INTL_CODESET_NONE;
  cinfo->coll_infer.coerc_level = PT_COLLATION_NOT_COERC;
  cinfo->coll_infer.can_force_cs = false;
  cinfo->prec = cinfo->scale = 0;
  cinfo->ref_att = att;
  cinfo->force_cast = false;

  cinfo->type_enum = att->type_enum;

  switch (att->type_enum)
    {
    case PT_TYPE_SMALLINT:
      cinfo->prec = 6;
      cinfo->scale = 0;
      break;
    case PT_TYPE_INTEGER:
      cinfo->prec = 10;
      cinfo->scale = 0;
      break;
    case PT_TYPE_BIGINT:
      cinfo->prec = 19;
      cinfo->scale = 0;
      break;
    case PT_TYPE_NUMERIC:
      cinfo->prec = (att->data_type) ? att->data_type->info.data_type.precision : 0;
      cinfo->scale = (att->data_type) ? att->data_type->info.data_type.dec_precision : 0;
      break;
    case PT_TYPE_CHAR:
    case PT_TYPE_VARCHAR:
      cinfo->prec = (att->data_type) ? att->data_type->info.data_type.precision : 0;
      cinfo->scale = 0;
      break;
    default:
      break;
    }

  if (PT_HAS_COLLATION (att->type_enum))
    {
      (void) pt_get_collation_info ((PT_NODE *) att, &(cinfo->coll_infer));
    }
}

/*
 * pt_get_common_type_for_union () -
 *   return:
 *   parser(in):
 *   att1(in):
 *   att2(in):
 *   cinfo(out):
 *   idx(in):
 *   need_cast(out):
 */
static PT_NODE *
pt_get_common_type_for_union (PARSER_CONTEXT * parser, PT_NODE * att1, PT_NODE * att2, SEMAN_COMPATIBLE_INFO * cinfo,
                  int idx, bool * need_cast)
{
  PT_NODE *dt1, *dt2;
  PT_TYPE_ENUM common_type;
  bool is_compatible = false;

  dt1 = att1->data_type;
  dt2 = att2->data_type;

  if ((PT_IS_STRING_TYPE (att1->type_enum) && att2->type_enum == PT_TYPE_MAYBE)
      || (PT_IS_STRING_TYPE (att2->type_enum) && att1->type_enum == PT_TYPE_MAYBE))
    {
      common_type = (att1->type_enum == PT_TYPE_MAYBE ? att2->type_enum : att1->type_enum);
    }
  else
    {
      common_type = pt_common_type (att1->type_enum, att2->type_enum);
    }

  if (common_type != PT_TYPE_NONE)
    {
      SEMAN_COMPATIBLE_INFO att1_info, att2_info;
      /* save attr idx and compatible type */
      cinfo->idx = idx;

      pt_get_compatible_info_from_node (att1, &att1_info);
      pt_get_compatible_info_from_node (att2, &att2_info);

      is_compatible = pt_update_compatible_info (parser, cinfo, common_type, &att1_info, &att2_info);

      if (is_compatible)
    {
      *need_cast = true;
    }
    }

  if (!is_compatible)
    {
      PT_ERRORmf2 (parser, att1, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UNION_INCOMPATIBLE,
           pt_short_print (parser, att1), pt_short_print (parser, att2));
      return NULL;
    }

  return att1;
}

/*
 * pt_check_union_compatibility () - check two query_specs for
 *                                   union compatibility
 *   return:  node on success, NULL otherwise.
 *   parser(in): the parser context used to derive qry1 and qry2
 *   node(in): a query node
 *
 * Note :
 *   the definition of union compatible is: same number of pairwise
 *   union-compatible attributes from the two query_specs.
 *   two vclass compatible attributes are considered union-compatible.
 */

PT_NODE *
pt_check_union_compatibility (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *attrs1, *attrs2, *result = node;
  PT_NODE *arg1, *arg2;
  int cnt1, cnt2;
  SEMAN_COMPATIBLE_INFO *cinfo = NULL;
  bool need_cast;

  assert (parser != NULL);

  if (node == NULL
      || (node->node_type != PT_UNION && node->node_type != PT_INTERSECTION && node->node_type != PT_DIFFERENCE
      && node->node_type != PT_CTE))
    {
      return NULL;
    }

  if (node->node_type == PT_CTE)
    {
      arg1 = node->info.cte.non_recursive_part;
      arg2 = node->info.cte.recursive_part;
    }
  else
    {
      arg1 = node->info.query.q.union_.arg1;
      arg2 = node->info.query.q.union_.arg2;
    }

  attrs1 = pt_get_select_list (parser, arg1);
  if (attrs1 == NULL)
    {
      return NULL;
    }
  attrs2 = pt_get_select_list (parser, arg2);
  if (attrs2 == NULL)
    {
      return NULL;
    }

  cnt1 = pt_length_of_select_list (attrs1, EXCLUDE_HIDDEN_COLUMNS);
  cnt2 = pt_length_of_select_list (attrs2, EXCLUDE_HIDDEN_COLUMNS);

  if (cnt1 != cnt2)
    {
      PT_ERRORmf2 (parser, attrs1, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ARITY_MISMATCH, cnt1, cnt2);
      return NULL;
    }

  /* get compatible info */
  cinfo = pt_get_compatible_info (parser, node, attrs1, attrs2, &need_cast);
  if (cinfo == NULL && need_cast)
    {
      result = NULL;
    }

  /* convert attrs type to compatible type */
  if (result && need_cast == true)
    {
      if (!pt_to_compatible_cast (parser, arg1, cinfo, cnt1) || !pt_to_compatible_cast (parser, arg2, cinfo, cnt1))
    {
      result = NULL;
    }
      else
    {
      /* copy the new data_type to the actual UNION node */
      if (node->data_type != NULL)
        {
          parser_free_tree (parser, node->data_type);
        }

      node->data_type = parser_copy_tree (parser, arg1->data_type);
    }
    }

  if (cinfo)
    {
      free_and_init (cinfo);
    }

  return result;
}

/*
 * pt_check_type_compatibility_of_values_query () - check rows for
 *                                          values query compatibility
 *   return:  node on success, NULL otherwise.
 *   parser(in): the parser context used to derive qry1 and qry2
 *   node(in): a query node
 *
 * Note :
 *   the definition of values query compatible is: same number of
 *   attributes from rows.
 *   every two rows' compatible attributes are considered union-compatible.
 */
PT_NODE *
pt_check_type_compatibility_of_values_query (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *node_list, *result = node;
  SEMAN_COMPATIBLE_INFO *cinfo = NULL;
  bool need_cast = false;
  PT_NODE *attrs;
  int count;

  assert (parser);

  if (node == NULL || node->node_type != PT_SELECT || !PT_IS_VALUE_QUERY (node))
    {
      return NULL;
    }

  node_list = node->info.query.q.select.list;
  if (node_list == NULL)
    {
      return NULL;
    }
  attrs = node_list->info.node_list.list;

  node_list = node_list->next;
  /* only one row */
  if (node_list == NULL)
    {
      return result;
    }

  count = pt_length_of_select_list (attrs, EXCLUDE_HIDDEN_COLUMNS);

  /* get compatible_info for cast */
  cinfo = pt_get_values_query_compatible_info (parser, node, &need_cast);

  if (cinfo == NULL && need_cast)
    {
      result = NULL;
      goto end;
    }

  /* convert attrs type to compatible type */
  if (need_cast && cinfo != NULL)
    {
      result = pt_values_query_to_compatible_cast (parser, node, cinfo, count);
    }

end:
  if (cinfo)
    {
      free_and_init (cinfo);
    }

  return result;
}

/*
 * pt_check_union_values_query_compatibility () - check compatibility
 *                                                when union values query
 *   return:  node on success, NULL otherwise.
 *   parser(in): the parser context used to derive qry1 and qry2
 *   node(in): a query node
 *
 * Note :
 *   please see pt_check_union_compatibility
 *   and pt_check_type_compatibility_of_values_query
 */
PT_NODE *
pt_check_union_type_compatibility_of_values_query (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *attrs1, *attrs2, *att1, *att2, *result = node;
  int cnt1, cnt2, i;
  SEMAN_COMPATIBLE_INFO *cinfo = NULL;
  SEMAN_COMPATIBLE_INFO *cinfo_arg1 = NULL;
  SEMAN_COMPATIBLE_INFO *cinfo_arg2 = NULL;
  SEMAN_COMPATIBLE_INFO *cinfo_arg3 = NULL;
  bool need_cast;
  PT_NODE *arg1, *arg2, *tmp;
  bool is_compatible;

  assert (parser != NULL);

  if (!node
      || !(node->node_type == PT_UNION || node->node_type == PT_INTERSECTION || node->node_type == PT_DIFFERENCE
       || node->node_type == PT_CTE))
    {
      return NULL;
    }

  if (node->node_type == PT_CTE)
    {
      arg1 = node->info.cte.non_recursive_part;
      arg2 = node->info.cte.recursive_part;
    }
  else
    {
      arg1 = node->info.query.q.union_.arg1;
      arg2 = node->info.query.q.union_.arg2;
    }

  if (!arg1 || !arg2 || !(attrs1 = pt_get_select_list (parser, arg1)) || !(attrs2 = pt_get_select_list (parser, arg2)))
    {
      return NULL;
    }

  cnt1 = pt_length_of_select_list (attrs1, EXCLUDE_HIDDEN_COLUMNS);
  cnt2 = pt_length_of_select_list (attrs2, EXCLUDE_HIDDEN_COLUMNS);
  if (cnt1 != cnt2)
    {
      PT_ERRORmf2 (parser, attrs1, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ARITY_MISMATCH, cnt1, cnt2);
      return NULL;
    }

  if (PT_IS_VALUE_QUERY (arg1) && PT_IS_VALUE_QUERY (arg2)) /* two values query */
    {
      need_cast = false;
      cinfo_arg1 = pt_get_values_query_compatible_info (parser, arg1, &need_cast);
      if (cinfo_arg1 == NULL && need_cast)
    {
      result = NULL;
      goto end;
    }

      need_cast = false;
      cinfo_arg2 = pt_get_values_query_compatible_info (parser, arg2, &need_cast);
      if (cinfo_arg2 == NULL && need_cast)
    {
      result = NULL;
      goto end;
    }

      /* compare cinfo_arg1 and cinfo_arg2 save the compatible cinfo in cinfo_arg1 */
      if (cinfo_arg1 != NULL && cinfo_arg2 != NULL)
    {
      is_compatible = true;

      for (i = 0, att1 = attrs1, att2 = attrs2; i < cnt1; ++i, att1 = att1->next, att2 = att2->next)
        {
          is_compatible = pt_combine_compatible_info (parser, cinfo_arg1 + i, cinfo_arg2 + i, att1, att2, i);
          if (!is_compatible)
        {
          result = NULL;
          goto end;
        }
        }
    }
      else if (cinfo_arg2 != NULL)
    {
      cinfo_arg1 = cinfo_arg2;
      cinfo_arg2 = NULL;
    }

      /* compare the select list */
      cinfo_arg3 = pt_get_compatible_info (parser, node, attrs1, attrs2, &need_cast);
      if (cinfo_arg3 == NULL && need_cast)
    {
      result = NULL;
      goto end;
    }

      if (need_cast)
    {
      if (cinfo_arg1 != NULL)
        {
          is_compatible = true;
          for (i = 0, att1 = attrs1, att2 = attrs2; i < cnt1; ++i, att1 = att1->next, att2 = att2->next)
        {
          is_compatible = pt_combine_compatible_info (parser, cinfo_arg1 + i, cinfo_arg3 + i, att1, att2, i);
          if (!is_compatible)
            {
              result = NULL;
              goto end;
            }
        }
        }
      else
        {
          cinfo_arg1 = cinfo_arg3;
          cinfo_arg3 = NULL;
        }
    }

      cinfo = cinfo_arg1;
    }
  else if (PT_IS_VALUE_QUERY (arg1) || PT_IS_VALUE_QUERY (arg2))    /* one values query, one select */
    {
      /* make arg1->is_value_query==1 */
      if (PT_IS_VALUE_QUERY (arg2))
    {
      tmp = arg1;
      arg1 = arg2;
      arg2 = tmp;
    }

      /* arg1 is the values query */
      need_cast = false;
      cinfo_arg1 = pt_get_values_query_compatible_info (parser, arg1, &need_cast);
      if (cinfo_arg1 == NULL && need_cast)
    {
      result = NULL;
      goto end;
    }

      /* get the cinfo of select */
      attrs1 = pt_get_select_list (parser, arg1);
      attrs2 = pt_get_select_list (parser, arg2);
      cinfo_arg2 = pt_get_compatible_info (parser, node, attrs1, attrs2, &need_cast);
      if (cinfo_arg2 == NULL && need_cast)
    {
      result = NULL;
      goto end;
    }

      if (need_cast)
    {
      /* compare cinfo_arg1 and cinfo_arg2 save the compatible cinfo in cinfo_arg1 */
      if (cinfo_arg1 != NULL)
        {
          is_compatible = true;
          for (i = 0, att1 = attrs1, att2 = attrs2; i < cnt1; ++i, att1 = att1->next, att2 = att2->next)
        {
          is_compatible = pt_combine_compatible_info (parser, cinfo_arg1 + i, cinfo_arg2 + i, att1, att2, i);
          if (!is_compatible)
            {
              result = NULL;
              goto end;
            }
        }
        }
      else
        {
          cinfo_arg1 = cinfo_arg2;
          cinfo_arg2 = NULL;
        }
      cinfo = cinfo_arg1;
    }
    }
  else
    {
      /* should not be here */
      assert (false);
    }

  /* make the cast */
  if (cinfo != NULL)
    {
      if (pt_to_compatible_cast (parser, arg1, cinfo, cnt1) == NULL
      || pt_to_compatible_cast (parser, arg2, cinfo, cnt1) == NULL)
    {
      result = NULL;
    }
    }

end:
  if (cinfo_arg1)
    {
      free_and_init (cinfo_arg1);
    }
  if (cinfo_arg2)
    {
      free_and_init (cinfo_arg2);
    }
  if (cinfo_arg3)
    {
      free_and_init (cinfo_arg3);
    }

  return result;
}

#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
/*
 * pt_make_default_value () -
 *   return:  return a PT_NODE for the default value
 *   parser(in): parser context
 *   class_name(in): class name of the attr to be defined a default value
 *   attr_name(in): name of attr to be defined a default value
 */
static PT_NODE *
pt_make_default_value (PARSER_CONTEXT * parser, const char *class_name, const char *attr_name)
{
  DB_OBJECT *class_obj;
  DB_ATTRIBUTE *attr_obj;
  DB_VALUE *value;
  PT_NODE *node = NULL;
  char *value_string;

  class_obj = db_find_class (class_name);
  if (class_obj)
    {
      attr_obj = db_get_attribute (class_obj, attr_name);
      if (attr_obj)
    {
      value = db_attribute_default (attr_obj);
      if (value)
        {
          value_string = db_get_string (value);
          node = pt_make_string_value (parser, value_string);
        }
    }
    }
  return node;
}

/*
 * pt_make_parameter () -
 *   return:  return a PT_NODE for the parameter name
 *   parser(in): parser context
 *   name(in): parameter name to make up a PT_NODE
 *   is_out_parameter(in): whether input or output parameter
 */

static PT_NODE *
pt_make_parameter (PARSER_CONTEXT * parser, const char *name, int is_out_parameter)
{
  PT_NODE *node;

  node = parser_new_node (parser, PT_NAME);
  if (node)
    {
      node->info.name.original = pt_append_string (parser, NULL, name);
      node->info.name.meta_class = PT_PARAMETER;
      if (is_out_parameter)
    {           /* to skip parameter binding */
      node->info.name.spec_id = (UINTPTR) node;
      node->info.name.resolved = pt_append_string (parser, NULL, "out parameter");
    }
    }
  return node;
}

/*
 * pt_append_statements_on_add_attribute () -
 *   return:  return a list of statement string or null on error
 *   parser(in): parser context
 *   statement_list(in,out): statement strings to be created internally
 *   stmt_node(in): parse tree for a create or alter statement
 *   class_name(in): class name to add a attr
 *   attr_name(in): attr name to add
 *   attr(in/out): attr definition to add
 *
 * Note :
 *   rewrite rule is like this.
 *     create class c (..., a text constraint, ...);
 *     => (pre) create class c_text_a_ under db_text;
 *     => (main) create class c (..., a c_text_a_, ...);
 *     => (post) alter class c_text_a_ add tid c unique, tdata string constraint;
 *     => (post) create unique index on c(a);
 *     => (post) grant select on c to user;
 */

static PT_NODE *
pt_append_statements_on_add_attribute (PARSER_CONTEXT * parser, PT_NODE * statement_list, PT_NODE * stmt_node,
                       const char *class_name, const char *attr_name, PT_NODE * attr)
{
  PT_NODE *s1, *s2, *s3, *s4;
  char *text_class_name = NULL, *stmt = NULL;
  char *constraint_name = NULL;

  text_class_name = pt_append_string (parser, NULL, class_name);
  text_class_name = pt_append_string (parser, text_class_name, "_text_");
  text_class_name = pt_append_string (parser, text_class_name, attr_name);

  constraint_name = pt_append_string (parser, NULL, TEXT_CONSTRAINT_PREFIX);
  constraint_name = pt_append_string (parser, constraint_name, attr_name);

  if (db_find_class (text_class_name))
    {
      PT_ERRORmf (parser, stmt_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_EXISTS, text_class_name);
      return NULL;
    }

  stmt = pt_append_string (parser, NULL, "CREATE CLASS ");
  stmt = pt_append_string (parser, stmt, text_class_name);
  stmt = pt_append_string (parser, stmt, " UNDER db_text;");
  s1 = pt_make_string_value (parser, stmt);

  stmt = pt_append_string (parser, NULL, "ALTER CLASS ");
  stmt = pt_append_string (parser, stmt, text_class_name);
  stmt = pt_append_string (parser, stmt, " ADD tid ");
  stmt = pt_append_string (parser, stmt, class_name);
  stmt = pt_append_string (parser, stmt, ", tdata STRING ");
  stmt = pt_append_string (parser, stmt, ((attr->info.attr_def.data_default)
                      ? parser_print_tree (parser, attr->info.attr_def.data_default) : ""));
  stmt = pt_append_string (parser, stmt, " ");
  stmt = pt_append_string (parser, stmt, ((attr->info.attr_def.constrain_not_null) ? "NOT NULL" : ""));
  stmt = pt_append_string (parser, stmt, ", CONSTRAINT ");
  stmt = pt_append_string (parser, stmt, constraint_name);
  stmt = pt_append_string (parser, stmt, " UNIQUE(tid)");
  stmt = pt_append_string (parser, stmt, ";");
  s2 = pt_make_string_value (parser, stmt);

  stmt = pt_append_string (parser, NULL, "CREATE UNIQUE INDEX ");
  stmt = pt_append_string (parser, stmt, constraint_name);
  stmt = pt_append_string (parser, stmt, " ON ");
  stmt = pt_append_string (parser, stmt, class_name);
  stmt = pt_append_string (parser, stmt, "([");
  stmt = pt_append_string (parser, stmt, attr_name);
  stmt = pt_append_string (parser, stmt, "]);");
  s3 = pt_make_string_value (parser, stmt);

  stmt = pt_append_string (parser, NULL, "GRANT SELECT ON ");
  stmt = pt_append_string (parser, stmt, text_class_name);
  stmt = pt_append_string (parser, stmt, " TO ");
  stmt = pt_append_string (parser, stmt, au_get_user_name (Au_user));
  stmt = pt_append_string (parser, stmt, " WITH GRANT OPTION");
  s4 = pt_make_string_value (parser, stmt);

  /* redefine the attribute definition */
  attr->type_enum = PT_TYPE_OBJECT;
  attr->data_type->type_enum = attr->type_enum;
  attr->data_type->info.data_type.entity = pt_name (parser, text_class_name);
  if (attr->data_type->info.data_type.entity == NULL)
    {
      PT_ERRORm (parser, attr, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      return NULL;
    }

  attr->data_type->info.data_type.units = 1;
  attr->data_type->info.data_type.precision = 0;

  parser_free_tree (parser, attr->info.attr_def.data_default);
  attr->info.attr_def.data_default = NULL;
  attr->info.attr_def.constrain_not_null = 0;

  /* indicate the time of doing statement, 'etc' points to the statement to do previously */
  s1->etc = NULL;
  s2->etc = stmt_node;
  s3->etc = stmt_node;
  s1->next = s2;
  s2->next = s3;
  s3->next = s4;

  if (statement_list)
    {
      s4->next = statement_list;
    }
  statement_list = s1;

  return statement_list;
}

/*
 * pt_append_statements_on_change_default () -
 *   return:  return a list of statement string or null on error
 *   parser(in): parser context
 *   statement_list(in,out): statement strings to be created internally
 *   stmt_node(in): parse tree for a alter default statement
 *   class_name(in): class name of a attr to redefine the default value
 *   attr_name(in): attr name to redefine the default value
 *   value(in/out): default value of the attr
 *
 * Note :
 *   rewrite rule is like this.
 *     alter class c change ..., a default value, ...;
 *     => (pre) alter class c_text_a_ change data default value;
 *     => (main) alter class c change ..., a default null, ...;
 */
static PT_NODE *
pt_append_statements_on_change_default (PARSER_CONTEXT * parser, PT_NODE * statement_list, PT_NODE * stmt_node,
                    const char *class_name, const char *attr_name, PT_NODE * value)
{
  PT_NODE *s1, *save_next;
  char *text_class_name = NULL, *stmt = NULL;

  text_class_name = pt_append_string (parser, NULL, class_name);
  text_class_name = pt_append_string (parser, text_class_name, "_text_");
  text_class_name = pt_append_string (parser, text_class_name, attr_name);

  if (!db_find_class (text_class_name))
    {
      PT_ERRORmf (parser, stmt_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_EXIST, text_class_name);
      return NULL;
    }

  stmt = pt_append_string (parser, NULL, "ALTER CLASS ");
  stmt = pt_append_string (parser, stmt, text_class_name);
  stmt = pt_append_string (parser, stmt, " CHANGE tdata DEFAULT ");
  stmt = pt_append_string (parser, stmt, parser_print_tree (parser, value));
  s1 = pt_make_string_value (parser, stmt);

  /* redefine the default value */
  save_next = value->next;
  parser_free_subtrees (parser, value);
  parser_reinit_node (value);
  value->type_enum = PT_TYPE_NULL;
  value->next = save_next;

  s1->etc = NULL;

  if (statement_list)
    {
      s1->next = statement_list;
    }
  statement_list = s1;

  return statement_list;
}

/*
 * pt_append_statements_on_drop_attributes () -
 *   return:  return a list of statement string or null on error
 *   parser(in): parser context
 *   statement_list(in/out): statement strings to be created internally
 *   class_name_list(in): a list of class name to drop
 *
 * Note :
 *   rewrite rule is like this.
 *     alter class c drop ..., a, ...;
 *     => (pre) drop class c_text_a_;
 *     => (main) alter class c drop ..., a, ...;
 *     drop class c;
 *     => (pre) drop class c_text_a_;
 *     => (main) drop class c;
 */

static PT_NODE *
pt_append_statements_on_drop_attributes (PARSER_CONTEXT * parser, PT_NODE * statement_list, const char *class_name_list)
{
  PT_NODE *s1;
  char *stmt = NULL;

  stmt = pt_append_string (parser, NULL, "DROP CLASS ");
  stmt = pt_append_string (parser, stmt, class_name_list);
  s1 = pt_make_string_value (parser, stmt);

  s1->etc = NULL;

  if (statement_list)
    {
      s1->next = statement_list;
    }
  statement_list = s1;

  return statement_list;
}

/*
 * pt_append_statements_on_insert () -
 *   return:  return a list of statement string or null on error
 *   parser(in): parser context
 *   stmt_node(in): parse tree for a insert statement
 *   class_name(in): class name to do insert
 *   attr_name(in): attr name to do insert
 *   value(in/out): value to do insert at the attr
 *   parameter(in): output parameter for the insert statement
 *
 * Note :
 *   rewrite rule is like this.
 *     insert into c (..., a, ...) values (..., v, ...);
 *     => (main) insert into c (..., a.object, ...) values (..., null, ...)
 *        into :obj1;
 *     => (post) insert into c_text_a_ values (:obj1, v) into :obj2;
 *     => (post) update c set a.object = :obj2 where c = :obj1;
 */
static PT_NODE *
pt_append_statements_on_insert (PARSER_CONTEXT * parser, PT_NODE * stmt_node, const char *class_name,
                const char *attr_name, PT_NODE * value, PT_NODE * parameter)
{
  PT_NODE *s1, *s2, *list;
  PT_NODE *save_next;
  char *text_class_name = NULL, *stmt = NULL;
  char param1_name[256], param2_name[256];
  char alias1_name[256];
  unsigned int save_custom;

  text_class_name = pt_append_string (parser, NULL, class_name);
  text_class_name = pt_append_string (parser, text_class_name, "_text_");
  text_class_name = pt_append_string (parser, text_class_name, attr_name);

  if (!db_find_class (text_class_name))
    {
      PT_ERRORmf (parser, stmt_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_EXIST, text_class_name);
      return NULL;
    }

  if (parameter && parameter->info.name.original)
    {
      sprintf (param1_name, "%s", parameter->info.name.original);
    }
  else
    {
      sprintf (param1_name, "%s_%p", "p1", stmt_node);
    }
  sprintf (param2_name, "%s_%p", "p2", stmt_node);
  sprintf (alias1_name, "%s_%p", "c1", stmt_node);

  save_custom = parser->custom_print;
  parser->custom_print = parser->custom_print | PT_INTERNAL_PRINT;

  stmt = pt_append_string (parser, NULL, "INSERT INTO ");
  stmt = pt_append_string (parser, stmt, text_class_name);
  stmt = pt_append_string (parser, stmt, " VALUES (:");
  stmt = pt_append_string (parser, stmt, param1_name);
  stmt = pt_append_string (parser, stmt, ", ");
  stmt = pt_append_string (parser, stmt, parser_print_tree (parser, value));
  stmt = pt_append_string (parser, stmt, ") INTO :");
  stmt = pt_append_string (parser, stmt, param2_name);
  stmt = pt_append_string (parser, stmt, "; ");
  s1 = pt_make_string_value (parser, stmt);

  parser->custom_print = save_custom;

  stmt = pt_append_string (parser, NULL, "UPDATE ");
  stmt = pt_append_string (parser, stmt, class_name);
  stmt = pt_append_string (parser, stmt, " ");
  stmt = pt_append_string (parser, stmt, alias1_name);
  stmt = pt_append_string (parser, stmt, " SET [");
  stmt = pt_append_string (parser, stmt, attr_name);
  stmt = pt_append_string (parser, stmt, "].OBJECT = :");
  stmt = pt_append_string (parser, stmt, param2_name);
  stmt = pt_append_string (parser, stmt, " WHERE ");
  stmt = pt_append_string (parser, stmt, alias1_name);
  stmt = pt_append_string (parser, stmt, ".OBJECT = :");
  stmt = pt_append_string (parser, stmt, param1_name);
  s2 = pt_make_string_value (parser, stmt);

  /* redefine the insert value */
  save_next = value->next;
  parser_free_subtrees (parser, value);
  parser_reinit_node (value);
  value->node_type = PT_VALUE;
  value->type_enum = PT_TYPE_NULL;
  value->next = save_next;

  s1->etc = stmt_node;
  s2->etc = stmt_node;
  s1->next = s2;

  list = stmt_node->info.insert.internal_stmts;
  if (list == NULL)
    {
      stmt_node->info.insert.internal_stmts = s1;
    }
  else
    {
      while (list->next != NULL)
    {
      list = list->next;
    }
      list->next = s1;
    }
  list = s1;

  return list;
}


/*
 * pt_append_statements_on_update () -
 *   return:  return a list of statement string or null on error
 *   parser(in): parser context
 *   stmt_node(in): parse tree for a update statement
 *   class_name(in): class name to do update
 *   attr_name(in): attr name to do update
 *   alias_name(in): alias for the class name
 *   value(in/out): value to do update at the attr
 *   where_ptr(in/out): pointer of a parse tree for the where clause of
 *                      the update statement
 *
 * Note :
 *   rewrite rule is like this.
 *     update c set ..., a = v, ... where condtion
 *     => (pre) select (select sum(set{a.object}) from c where condition)
 *         into :obj1 from db_root
 *     => (pre) update c_text_a_ set tdata = v where tid in
 *         (select c from c where a.object in :obj1)
 *     => (main) update c set ..., a.object = a.object, ...
 *         where a.object in :obj1
 */
static PT_NODE *
pt_append_statements_on_update (PARSER_CONTEXT * parser, PT_NODE * stmt_node, const char *class_name,
                const char *attr_name, const char *alias_name, PT_NODE * value, PT_NODE ** where_ptr)
{
  PT_NODE *s1, *s2, *list;
  PT_NODE *save_next;
  DB_VALUE *param1_dbvalp;
  char *text_class_name = NULL, *stmt = NULL;
  char param1_name[256];
  char alias1_name[256];
  unsigned int save_custom;

  text_class_name = pt_append_string (parser, NULL, class_name);
  text_class_name = pt_append_string (parser, text_class_name, "_text_");
  text_class_name = pt_append_string (parser, text_class_name, attr_name);

  if (!db_find_class (text_class_name))
    {
      PT_ERRORmf (parser, stmt_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_EXIST, text_class_name);
      return NULL;
    }

  sprintf (param1_name, "%s_%p", "p1", attr_name);
  sprintf (alias1_name, "%s_%p", "c1", attr_name);

  save_custom = parser->custom_print;
  parser->custom_print = parser->custom_print | PT_INTERNAL_PRINT;

  stmt = pt_append_string (parser, NULL, "SELECT {null}+(SELECT SUM(SET{[");
  stmt = pt_append_string (parser, stmt, attr_name);
  stmt = pt_append_string (parser, stmt, "].OBJECT}) FROM ");
  stmt = pt_append_string (parser, stmt, class_name);
  stmt = pt_append_string (parser, stmt, " ");
  stmt = pt_append_string (parser, stmt, alias_name);
  if (*where_ptr)
    {
      stmt = pt_append_string (parser, stmt, " WHERE ");
      stmt = pt_append_string (parser, stmt, parser_print_tree (parser, *where_ptr));
    }
  stmt = pt_append_string (parser, stmt, ") INTO :");
  stmt = pt_append_string (parser, stmt, param1_name);
  stmt = pt_append_string (parser, stmt, " FROM db_root;");
  s1 = pt_make_string_value (parser, stmt);

  /* To resolve out parameter at compile time, put the parameter into the label table with null value */
  param1_dbvalp = db_value_create ();
  if (param1_dbvalp == NULL)
    {
      parser->custom_print = save_custom;
      return NULL;
    }
  else
    {
      db_make_set (param1_dbvalp, db_set_create_basic (NULL, NULL));
      if (pt_associate_label_with_value (param1_name, param1_dbvalp) != NO_ERROR)
    {
      parser->custom_print = save_custom;
      return NULL;
    }
    }

  stmt = pt_append_string (parser, NULL, "UPDATE ");
  stmt = pt_append_string (parser, stmt, text_class_name);
  stmt = pt_append_string (parser, stmt, " SET tdata = ");
  stmt = pt_append_string (parser, stmt, ((value->node_type == PT_NAME && value->info.name.meta_class == PT_NORMAL)
                      ? "tid." : ""));
  stmt = pt_append_string (parser, stmt, parser_print_tree (parser, value));
  stmt = pt_append_string (parser, stmt, " WHERE tid IN (SELECT ");
  stmt = pt_append_string (parser, stmt, alias1_name);
  stmt = pt_append_string (parser, stmt, " FROM ");
  stmt = pt_append_string (parser, stmt, class_name);
  stmt = pt_append_string (parser, stmt, " ");
  stmt = pt_append_string (parser, stmt, alias1_name);
  stmt = pt_append_string (parser, stmt, " WHERE [");
  stmt = pt_append_string (parser, stmt, attr_name);
  stmt = pt_append_string (parser, stmt, "].OBJECT IN :");
  stmt = pt_append_string (parser, stmt, param1_name);
  stmt = pt_append_string (parser, stmt, ")");
  s2 = pt_make_string_value (parser, stmt);

  parser->custom_print = save_custom;

  /* redefine where clause if the clause is redefined at first */
  if ((*where_ptr) == NULL || (*where_ptr)->etc != (*where_ptr))
    {
      if (*where_ptr)
    {
      parser_free_tree (parser, *where_ptr);
    }
      *where_ptr = parser_new_node (parser, PT_EXPR);
      if (*where_ptr == NULL)
    {
      return NULL;
    }
      (*where_ptr)->info.expr.op = PT_IS_IN;
      (*where_ptr)->info.expr.arg1 = pt_name (parser, attr_name);
      (*where_ptr)->info.expr.arg2 = pt_make_parameter (parser, param1_name, 0);
      (*where_ptr)->etc = (*where_ptr); /* mark to prevent multiple rewrite */
      PT_NAME_INFO_SET_FLAG ((*where_ptr)->info.expr.arg1, PT_NAME_INFO_EXTERNAL);
    }

  /* redefine the assignment value */
  save_next = value->next;
  parser_free_subtrees (parser, value);
  parser_reinit_node (value);
  value->node_type = PT_NAME;
  value->info.name.original = pt_append_string (parser, NULL, attr_name);
  PT_NAME_INFO_SET_FLAG (value, PT_NAME_INFO_EXTERNAL);
  value->next = save_next;

  s1->etc = NULL;
  s2->etc = NULL;
  s1->next = s2;

  list = stmt_node->info.update.internal_stmts;
  if (list == NULL)
    {
      stmt_node->info.insert.internal_stmts = s1;
    }
  else
    {
      while (list->next != NULL)
    list = list->next;
      list->next = s1;
    }
  list = s1;

  parser->custom_print = save_custom;

  return list;
}

/*
 * pt_append_statements_on_delete () -
 *   return:  return a list of statement string or null on error
 *   parser(in): parser context
 *   stmt_node(in): parse tree for a delete statement
 *   class_name(in): class name to do delete
 *   attr_name(in): attr name to do delete
 *   alias_name(in): alias for the class name
 *   where_ptr(in/out): pointer of a parse tree for the where clause of
 *                      the delete statement
 *
 * Note :
 * rewrite rule is like this.
 *   delete from c where condition;
 *   => (pre) select (select sum(set{a.object}) from c where condition)
 *            into :obj1 from db_root
 *   => (pre) delete from c_text_a_ where tid in (select c from c where
 *            a.object in :obj1)
 *   => (main) delete from c where a.object in :obj1
 */

static PT_NODE *
pt_append_statements_on_delete (PARSER_CONTEXT * parser, PT_NODE * stmt_node, const char *class_name,
                const char *attr_name, const char *alias_name, PT_NODE ** where_ptr)
{
  PT_NODE *s1, *s2, *list;
  DB_VALUE *param1_dbvalp;
  char *text_class_name = NULL, *stmt = NULL;
  char param1_name[256];
  char alias1_name[256];
  unsigned int save_custom;

  text_class_name = pt_append_string (parser, NULL, class_name);
  text_class_name = pt_append_string (parser, text_class_name, "_text_");
  text_class_name = pt_append_string (parser, text_class_name, attr_name);

  if (!db_find_class (text_class_name))
    {
      PT_ERRORmf (parser, stmt_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_EXIST, text_class_name);
      return NULL;
    }

  sprintf (param1_name, "%s_%p", "p1", attr_name);
  sprintf (alias1_name, "%s_%p", "c1", attr_name);

  save_custom = parser->custom_print;
  parser->custom_print = parser->custom_print | PT_INTERNAL_PRINT;

  stmt = pt_append_string (parser, NULL, "SELECT {null}+(SELECT SUM(SET{[");
  stmt = pt_append_string (parser, stmt, attr_name);
  stmt = pt_append_string (parser, stmt, "].OBJECT}) FROM ");
  stmt = pt_append_string (parser, stmt, class_name);
  stmt = pt_append_string (parser, stmt, " ");
  stmt = pt_append_string (parser, stmt, alias_name);
  if (*where_ptr)
    {
      stmt = pt_append_string (parser, stmt, " WHERE ");
      stmt = pt_append_string (parser, stmt, parser_print_tree (parser, *where_ptr));
    }
  stmt = pt_append_string (parser, stmt, ") INTO :");
  stmt = pt_append_string (parser, stmt, param1_name);
  stmt = pt_append_string (parser, stmt, " FROM db_root;");
  s1 = pt_make_string_value (parser, stmt);

  parser->custom_print = save_custom;

  /* To resolve out parameter at compile time, put the parameter into the label table with null value */
  param1_dbvalp = db_value_create ();
  if (param1_dbvalp == NULL)
    {
      return NULL;
    }
  else
    {
      db_make_set (param1_dbvalp, db_set_create_basic (NULL, NULL));
      if (pt_associate_label_with_value (param1_name, param1_dbvalp) != NO_ERROR)
    {
      return NULL;
    }
    }
  stmt = pt_append_string (parser, NULL, "DELETE FROM ");
  stmt = pt_append_string (parser, stmt, text_class_name);
  stmt = pt_append_string (parser, stmt, " WHERE tid IN (SELECT ");
  stmt = pt_append_string (parser, stmt, alias1_name);
  stmt = pt_append_string (parser, stmt, " FROM ");
  stmt = pt_append_string (parser, stmt, class_name);
  stmt = pt_append_string (parser, stmt, " ");
  stmt = pt_append_string (parser, stmt, alias1_name);
  stmt = pt_append_string (parser, stmt, " WHERE [");
  stmt = pt_append_string (parser, stmt, attr_name);
  stmt = pt_append_string (parser, stmt, "].OBJECT IN :");
  stmt = pt_append_string (parser, stmt, param1_name);
  stmt = pt_append_string (parser, stmt, ")");
  s2 = pt_make_string_value (parser, stmt);

  /* redefine where clause if the clause is redefined at first */
  if ((*where_ptr) == NULL || (*where_ptr)->etc != (*where_ptr))
    {
      if (*where_ptr)
    {
      parser_free_tree (parser, *where_ptr);
    }
      if ((*where_ptr = parser_new_node (parser, PT_EXPR)) == NULL)
    {
      return NULL;
    }
      (*where_ptr)->info.expr.op = PT_IS_IN;
      (*where_ptr)->info.expr.arg1 = pt_name (parser, attr_name);
      (*where_ptr)->info.expr.arg2 = pt_make_parameter (parser, param1_name, 0);
      (*where_ptr)->etc = (*where_ptr); /* mark to prevent multiple rewrite */
      PT_NAME_INFO_SET_FLAG ((*where_ptr)->info.expr.arg1, PT_NAME_INFO_EXTERNAL);
    }

  s1->etc = NULL;
  s2->etc = NULL;
  s1->next = s2;

  list = stmt_node->info.delete_.internal_stmts;
  if (list == NULL)
    {
      stmt_node->info.insert.internal_stmts = s1;
    }
  else
    {
      while (list->next != NULL)
    list = list->next;
      list->next = s1;
    }
  list = s1;

  return list;
}

/*
 * pt_resolve_insert_external () - create internal statements and
 *      rewrite a value to insert for TEXT typed attrs on into clause
 *      of a insert statement
 *   return:  none
 *   parser(in): parser context
 *   insert(in): parse tree of a insert statement
 */
static void
pt_resolve_insert_external (PARSER_CONTEXT * parser, PT_NODE * insert)
{
  PT_NODE *a, *v, *lhs, *rhs, *save_next;
  PT_NODE *spec, *entity, *value;
  const char *class_name, *attr_name;
  char *text_class_name = NULL, param1_name[256];

  spec = insert->info.insert.spec;
  entity = (spec ? spec->info.spec.entity_name : NULL);
  class_name = (entity ? entity->info.name.original : NULL);
  if (class_name == NULL)
    {
      return;
    }

  a = insert->info.insert.attr_list;

  if (insert->info.insert.is_value == PT_IS_SUBQUERY)
    {
      for (; a != NULL; a = a->next)
    {
      if (PT_IS_DOT_NODE (a))
        {
          PT_ERRORmf2 (parser, a, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CANT_COERCE_TO, "subquery", "text");
          return;
        }
    }
    }
  else if (insert->info.insert.is_value == PT_IS_DEFAULT_VALUE)
    {
      for (; a != NULL; a = a->next)
    {
      if (PT_IS_DOT_NODE (a))
        {
          /* replace "attr.tdata" with "attr" */
          save_next = a->next;
          lhs = a->info.expr.arg1;
          rhs = a->info.expr.arg2;
          *a = *lhs;
          a->next = save_next;
          parser_reinit_node (lhs); /* not to free subtrees */
          parser_free_tree (parser, lhs);
          parser_free_tree (parser, rhs);

          /* make a default value */
          attr_name = a->info.name.original;
          text_class_name = pt_append_string (parser, NULL, class_name);
          text_class_name = pt_append_string (parser, text_class_name, "_text_");
          text_class_name = pt_append_string (parser, text_class_name, attr_name);

          if ((value = pt_make_default_value (parser, text_class_name, "tdata")) == NULL)
        {
          goto exit_on_error;
        }

          if (insert->info.insert.into_var == NULL)
        {
          sprintf (param1_name, "p1_%p", insert);
          insert->info.insert.into_var = pt_make_parameter (parser, param1_name, 1);
        }
          if (pt_append_statements_on_insert (parser, insert, class_name, attr_name, value,
                          insert->info.insert.into_var) == NULL)
        {
          goto exit_on_error;
        }
        }
    }
    }
  else
    {
      v = insert->info.insert.value_clause;
      for (; a != NULL && v != NULL; a = a->next, v = v->next)
    {
      if (PT_IS_DOT_NODE (a))
        {
          /* replace "attr.tdata" to "attr" */
          save_next = a->next;
          lhs = a->info.expr.arg1;
          rhs = a->info.expr.arg2;
          *a = *lhs;
          a->next = save_next;
          parser_reinit_node (lhs); /* not to free subtrees */
          parser_free_tree (parser, lhs);
          parser_free_tree (parser, rhs);

          /* if (pt_assignment_compatible(parser, attr, v)) */
          attr_name = a->info.name.original;
          if (a->type_enum != v->type_enum)
        {
          if (insert->info.insert.into_var == NULL)
            {
              sprintf (param1_name, "p1_%p", insert);
              insert->info.insert.into_var = pt_make_parameter (parser, param1_name, 1);
            }
          if (pt_append_statements_on_insert (parser, insert, class_name, attr_name, v,
                              insert->info.insert.into_var) == NULL)
            {
              goto exit_on_error;
            }
        }
        }
    }
    }
  return;

exit_on_error:

  PT_ERRORm (parser, insert, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
  return;
}

/*
 * pt_resolve_update_external () - create internal statements and redefine
 *      a value to do update for TEXT typed attrs on assignment clause of
 *      a update statement
 *   return:  none
 *   parser(in): parser context
 *   update(in): parse tree of a update statement
 */
static void
pt_resolve_update_external (PARSER_CONTEXT * parser, PT_NODE * update)
{
  PT_NODE *a, *lhs, *rhs;
  PT_NODE *spec, *entity, *alias;
  DB_OBJECT *db_obj;
  DB_ATTRIBUTE *db_att;
  const char *class_name, *attr_name, *alias_name;

  spec = update->info.update.spec;
  entity = (spec ? spec->info.spec.entity_name : NULL);
  class_name = (entity ? entity->info.name.original : NULL);
  alias = (spec ? spec->info.spec.range_var : NULL);
  alias_name = (alias ? alias->info.name.original : NULL);

  if (class_name && (db_obj = db_find_class (class_name)))
    {
      for (a = update->info.update.assignment; a; a = a->next)
    {
      if (PT_IS_ASSIGN_NODE (a) && (lhs = a->info.expr.arg1) != NULL && (rhs = a->info.expr.arg2) != NULL)
        {
          if (PT_IS_NAME_NODE (lhs) && !PT_NAME_INFO_IS_FLAGED (lhs, PT_NAME_INFO_EXTERNAL))
        {
          attr_name = lhs->info.name.original;
          db_att = db_get_attribute (db_obj, attr_name);

          if (db_att && sm_has_text_domain (db_att, 0))
            {
              PT_NAME_INFO_SET_FLAG (lhs, PT_NAME_INFO_EXTERNAL);
              if (pt_append_statements_on_update (parser, update, class_name, attr_name, alias_name, rhs,
                              &update->info.update.search_cond) == NULL)
            {
              goto exit_on_error;
            }
            }
        }
        }
    }
    }

  return;

exit_on_error:

  PT_ERRORm (parser, update, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
  return;
}

/*
 * pt_resolve_delete_external () - create internal statements
 *      for TEXT typed attrs defined in class to do delete statement
 *   return:  none
 *   parser(in): parser context
 *   delete(in): parse tree of a delete statement
 */
static void
pt_resolve_delete_external (PARSER_CONTEXT * parser, PT_NODE * delete)
{
  PT_NODE *spec, *entity, *alias;
  DB_OBJECT *db_obj;
  DB_ATTRIBUTE *db_att;
  const char *class_name, *alias_name;

  spec = delete->info.delete_.spec;
  entity = (spec ? spec->info.spec.entity_name : NULL);
  class_name = (entity ? entity->info.name.original : NULL);
  alias = (spec ? spec->info.spec.range_var : NULL);
  alias_name = (alias ? alias->info.name.original : NULL);

  if (class_name && (db_obj = db_find_class (class_name)))
    {
      db_att = db_get_attributes_force (db_obj);
      while (db_att)
    {
      if (sm_has_text_domain (db_att, 0))
        {
          if (pt_append_statements_on_delete (parser, delete, class_name, db_attribute_name (db_att), alias_name,
                          &delete->info.delete_.search_cond) == NULL)
        {
          goto exit_on_error;
        }
        }
      db_att = db_attribute_next (db_att);
    }
    }

  return;

exit_on_error:

  PT_ERRORm (parser, delete, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
  return;
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * pt_resolve_default_external () - create internal statements
 *      for a TEXT typed attr to alter the default value
 *   return:  none
 *   parser(in): parser context
 *   alter(in): parse tree of a alter statement
 */
static void
pt_resolve_default_external (PARSER_CONTEXT * parser, PT_NODE * alter)
{
  PT_NODE *attr_name_list, *data_default_list, *stmt_list;
  PT_NODE *a, *v;
  PT_NODE *entity_name;
  DB_OBJECT *class_;
  DB_ATTRIBUTE *attr;
  const char *class_name;

  attr_name_list = alter->info.alter.alter_clause.ch_attr_def.attr_name_list;
  data_default_list = alter->info.alter.alter_clause.ch_attr_def.data_default_list;

  entity_name = alter->info.alter.entity_name;
  class_name = (entity_name ? entity_name->info.name.original : NULL);
  if (class_name && (class_ = db_find_class (class_name)) != NULL)
    {
      stmt_list = alter->info.alter.internal_stmts;
      for (a = attr_name_list, v = data_default_list; a != NULL && v != NULL; a = a->next, v = v->next)
    {
      attr = db_get_attribute (class_, a->info.name.original);
#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
      if (attr && sm_has_text_domain (attr, 0))
        {
          stmt_list =
        pt_append_statements_on_change_default (parser, stmt_list, alter, class_name, a->info.name.original, v);
          if (stmt_list == NULL)
        {
          PT_ERRORm (parser, alter, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
        }
        }
#endif /* ENABLE_UNUSED_FUNCTION */
    }
      alter->info.alter.internal_stmts = stmt_list;
    }

  return;
}

/*
 * pt_check_data_default () - checks data_default for semantic errors
 *
 * result            : modified data_default
 * parser(in)            : parser context
 * data_default_list(in) : data default node
 */
static PT_NODE *
pt_check_data_default (PARSER_CONTEXT * parser, PT_NODE * data_default_list)
{
  PT_NODE *result;
  PT_NODE *default_value;
  PT_NODE *save_next;
  PT_NODE *node_ptr;
  PT_NODE *data_default;
  PT_NODE *prev;
  bool has_query;

  if (pt_has_error (parser))
    {
      /* do nothing */
      return data_default_list;
    }

  if (data_default_list == NULL || data_default_list->node_type != PT_DATA_DEFAULT)
    {
      /* do nothing */
      return data_default_list;
    }

  prev = NULL;
  has_query = false;
  for (data_default = data_default_list; data_default; data_default = data_default->next)
    {
      save_next = data_default->next;
      data_default->next = NULL;

      default_value = data_default->info.data_default.default_value;

      (void) parser_walk_tree (parser, default_value, pt_find_query, &has_query, NULL, NULL);
      if (has_query)
    {
      PT_ERRORm (parser, default_value, MSGCAT_SET_PARSER_SEMANTIC,
             MSGCAT_SEMANTIC_SUBQUERY_NOT_ALLOWED_IN_DEFAULT_CLAUSE);
      /* skip other checks */
      goto end;
    }

      result = pt_semantic_type (parser, data_default, NULL);
      if (result != NULL)
    {
      /* change data_default */
      if (prev)
        {
          prev->next = result;
        }
      else
        {
          data_default_list = result;
        }
      data_default = result;
    }
      else
    {
      /* an error has occurred, skip other checks */
      goto end;
    }

      node_ptr = NULL;
      (void) parser_walk_tree (parser, default_value, pt_find_default_expression, &node_ptr, NULL, NULL);
      if (node_ptr != NULL && node_ptr != default_value)
    {
      /* nested default expressions are not supported */
      PT_ERRORmf (parser, node_ptr, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DEFAULT_NESTED_EXPR_NOT_ALLOWED,
              pt_show_binopcode (node_ptr->info.expr.op));
      goto end;
    }

      if (PT_IS_EXPR_NODE (default_value) && default_value->info.expr.op == PT_TO_CHAR
      && PT_IS_EXPR_NODE (default_value->info.expr.arg1))
    {
      int op_type = -1;

      if (PT_IS_EXPR_NODE (default_value->info.expr.arg2))
        {
          /* nested expressions in arg2 are not supported */
          op_type = default_value->info.expr.arg2->info.expr.op;
        }
      else if (node_ptr == NULL)
        {
          /* nested expressions in arg1 are not supported except sys date, time and user. */
          op_type = default_value->info.expr.arg1->info.expr.op;
        }

      if (op_type != -1)
        {
          PT_ERRORmf (parser, node_ptr, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DEFAULT_NESTED_EXPR_NOT_ALLOWED,
              pt_show_binopcode ((PT_OP_TYPE) op_type));
          goto end;
        }
    }

      node_ptr = NULL;
      parser_walk_tree (parser, default_value, pt_find_aggregate_function, &node_ptr, NULL, NULL);
      if (node_ptr != NULL)
    {
      PT_ERRORmf (parser,
              node_ptr,
              MSGCAT_SET_PARSER_SEMANTIC,
              MSGCAT_SEMANTIC_DEFAULT_EXPR_NOT_ALLOWED,
              fcode_get_lowercase_name (node_ptr->info.function.function_type));
      goto end;
    }

      node_ptr = NULL;
      parser_walk_tree (parser, default_value, pt_find_method_call, &node_ptr, NULL, NULL);
      if (node_ptr != NULL)
    {
      PT_ERRORmf (parser,
              node_ptr,
              MSGCAT_SET_PARSER_SEMANTIC,
              MSGCAT_SEMANTIC_DEFAULT_EXPR_NOT_ALLOWED, parser_print_tree (parser, default_value));
      goto end;
    }

    end:
      data_default->next = save_next;
      prev = data_default;
    }

  return data_default_list;
}

/*
 * pt_attr_check_default_cs_coll () - checks attribute's collation and
 *            codeset. If necessary, they are replaced with the
 *            ones passed as arguments.
 *
 * parser(in): parser context
 * attr(in/out) : data default node
 * default_cs(in): codeset of the attribute's class, or override value
 *         if special value = -1 is given, then charset implied by
 *         default_coll argument is used
 * default_coll(in): collation of the attribute's class, or override value
 */
int
pt_attr_check_default_cs_coll (PARSER_CONTEXT * parser, PT_NODE * attr, int default_cs, int default_coll)
{
  int attr_cs = attr->data_type->info.data_type.units;
  int attr_coll = attr->data_type->info.data_type.collation_id;
  LANG_COLLATION *lc;
  int err = NO_ERROR;

  assert (default_coll >= 0);

  if (attr->data_type->info.data_type.has_cs_spec)
    {
      if (attr->data_type->info.data_type.has_coll_spec == false)
    {
      /* use binary collation of attribute's charset specifier */
      attr_coll = LANG_GET_BINARY_COLLATION (attr_cs);
    }
    }
  else if (attr->data_type->info.data_type.has_coll_spec)
    {
      lc = lang_get_collation (attr_coll);
      assert (lc != NULL);
      attr_cs = lc->codeset;
    }
  else
    {
      /* attribute does not have a codeset or collation spec; use defaults */
      attr_coll = default_coll;
      if (default_cs == -1)
    {
      lc = lang_get_collation (default_coll);
      assert (lc != NULL);
      attr_cs = lc->codeset;
    }
      else
    {
      attr_cs = default_cs;
    }
    }

  if (attr->type_enum == PT_TYPE_ENUMERATION && attr->data_type != NULL)
    {
      /* coerce each element of enum to actual attribute codeset */
      PT_NODE *elem = NULL;

      elem = attr->data_type->info.data_type.enumeration;
      while (elem != NULL)
    {
      assert (elem->node_type == PT_VALUE);
      assert (PT_HAS_COLLATION (elem->type_enum));

      if ((elem->data_type != NULL && elem->data_type->info.data_type.units != attr_cs)
          || (elem->data_type == NULL && attr_cs != LANG_SYS_CODESET))
        {
          PT_NODE *dt;

          if (elem->data_type != NULL)
        {
          dt = parser_copy_tree (parser, elem->data_type);
        }
          else
        {
          dt = parser_new_node (parser, PT_DATA_TYPE);
          dt->type_enum = elem->type_enum;
          dt->info.data_type.precision = DB_DEFAULT_PRECISION;
        }
          dt->info.data_type.collation_id = attr_coll;
          dt->info.data_type.units = attr_cs;

          if (attr_cs == INTL_CODESET_RAW_BYTES)
        {
          /* conversion from multi-byte to binary must keep text */
          if (elem->info.value.data_value.str != NULL)
            {
              dt->info.data_type.precision = elem->info.value.data_value.str->length;
            }
          else if (elem->info.value.db_value_is_initialized)
            {
              dt->info.data_type.precision = db_get_string_size (&(elem->info.value.db_value));
            }
        }

          err = pt_coerce_value (parser, elem, elem, elem->type_enum, dt);
          if (err != NO_ERROR)
        {
          return err;
        }

          parser_free_tree (parser, dt);
        }
      elem = elem->next;
    }
    }

  attr->data_type->info.data_type.units = attr_cs;
  attr->data_type->info.data_type.collation_id = attr_coll;

  return err;
}

/*
 * pt_find_method_call () - search for a method call
 *
 * result     : parser tree node
 * parser(in)     : parser
 * tree(in)   : parser tree node
 * arg(in/out)    : true, if the stored procedure is found
 * continue_walk  : Continue walk.
 */
static PT_NODE *
pt_find_method_call (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
  PT_NODE **sp = (PT_NODE **) arg;

  if (tree == NULL)
    {
      *continue_walk = PT_STOP_WALK;
    }

  if (tree->node_type == PT_METHOD_CALL || (tree->node_type == PT_FUNCTION && tree->info.function.function_type == PT_GENERIC)  /* not resolved yet */
    )
    {
      *sp = tree;
      *continue_walk = PT_STOP_WALK;
    }

  return tree;
}

/*
 * pt_find_query () - search for a query
 *
 * result     : parser tree node
 * parser(in)     : parser
 * tree(in)   : parser tree node
 * arg(in/out)    : true, if the query is found
 * continue_walk  : Continue walk.
 */
static PT_NODE *
pt_find_query (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
  bool *has_query = (bool *) arg;
  assert (has_query != NULL);

  if (PT_IS_QUERY (tree))
    {
      *has_query = true;
      *continue_walk = PT_STOP_WALK;
    }

  return tree;
}


/*
 * pt_find_default_expression () - find a default expression
 *
 * result     :
 * parser(in)     :
 * tree(in)   :
 * arg(in)    : will point to default expression if any is found
 * continue_walk  :
 */
static PT_NODE *
pt_find_default_expression (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
  PT_NODE **default_expr = (PT_NODE **) arg, *node = NULL;

  if (tree == NULL || !PT_IS_EXPR_NODE (tree))
    {
      *continue_walk = PT_STOP_WALK;
      return tree;
    }

  if (tree->info.expr.op == PT_TO_CHAR && tree->info.expr.arg1 != NULL && PT_IS_EXPR_NODE (tree->info.expr.arg1))
    {
      /* The correctness of TO_CHAR expression is done a little bit later after obtaining system time. */
      assert (tree->info.expr.arg2 != NULL);
      node = tree->info.expr.arg1;
    }
  else
    {
      node = tree;
    }

  switch (node->info.expr.op)
    {
    case PT_SYS_TIME:
    case PT_SYS_DATE:
    case PT_SYS_DATETIME:
    case PT_SYS_TIMESTAMP:
    case PT_CURRENT_TIME:
    case PT_CURRENT_DATE:
    case PT_CURRENT_TIMESTAMP:
    case PT_CURRENT_DATETIME:
    case PT_USER:
    case PT_CURRENT_USER:
    case PT_UNIX_TIMESTAMP:
      *default_expr = tree;
      *continue_walk = PT_STOP_WALK;
      break;

    default:
      break;
    }

  return tree;
}

/*
 * pt_find_aggregate_function () - check if current expression contains an
 *                  aggregate function
 *
 * result     :
 * parser(in)     :
 * tree(in)   :
 * arg(in)    : will point to an aggregate function if any is found
 * continue_walk  :
 */
static PT_NODE *
pt_find_aggregate_function (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
  PT_NODE **agg_function = (PT_NODE **) arg;

  if (tree == NULL || (!PT_IS_EXPR_NODE (tree) && !PT_IS_FUNCTION (tree)))
    {
      *continue_walk = PT_STOP_WALK;
    }

  if (pt_is_aggregate_function (parser, tree))
    {
      *agg_function = tree;
      *continue_walk = PT_STOP_WALK;
    }

  return tree;
}

/*
 * pt_find_aggregate_analytic_pre ()
 *                 - check if current expression contains an aggregate or
 *                   analytic function
 *
 * result         :
 * parser(in)     :
 * tree(in)       :
 * arg(in)        : will point to the function if any is found
 * continue_walk  :
 */
static PT_NODE *
pt_find_aggregate_analytic_pre (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
  PT_NODE **function = (PT_NODE **) arg;

  if (*continue_walk == PT_STOP_WALK)
    {
      return tree;
    }

  assert (*function == NULL);

  if (tree && PT_IS_QUERY_NODE_TYPE (tree->node_type))
    {
      PT_NODE *find = NULL;

      /* For sub-queries, searching is limited to range of WHERE clause */
      find = pt_find_aggregate_analytic_in_where (parser, tree);
      if (find)
    {
      *function = find;
    }

      /*
       * Don't search children nodes of this query node, since
       * pt_find_aggregate_analytic_in_where already did it.
       * We may continue walking to search in the rest parts,
       * See pt_find_aggregate_analytic_post.
       */
      *continue_walk = PT_STOP_WALK;
    }
  else if (PT_IS_FUNCTION (tree))
    {
      if (pt_is_aggregate_function (parser, tree) || pt_is_analytic_function (parser, tree))
    {
      *function = tree;
      *continue_walk = PT_STOP_WALK;
    }
    }

  return tree;
}

/*
 * pt_find_aggregate_analytic_post ()
 *
 * result         :
 * parser(in)     :
 * tree(in)       :
 * arg(in)        :
 * continue_walk  :
 */
static PT_NODE *
pt_find_aggregate_analytic_post (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk)
{
  PT_NODE **function = (PT_NODE **) arg;

  if (tree && PT_IS_QUERY_NODE_TYPE (tree->node_type) && *function == NULL)
    {
      /* Need to search the rest part of tree */
      *continue_walk = PT_CONTINUE_WALK;
    }
  return tree;
}

/*
 * pt_find_aggregate_analytic_in_where ()
 *                 - find an aggregate or analytic function in where clause
 *
 * result         : point to the found function if any; NULL otherwise
 * parser(in)     :
 * node(in)       :
 * continue_walk  :
 *
 * [Note]
 * This function will search whether an aggregate or analytic function exists
 * in WHERE clause of below statements:
 *     INSERT, UPDATE, DO, SET, DELETE, SELECT, UNION, DIFFERENCE, INTERSECTION, and
 *     MERGE.
 * It stops searching when meets the first aggregate or analytic function.
 *
 * 1) For below node types, searching is limited to child node who containing
 *    SET clause:
 *     PT_UPDATE
 *    WHERE clause:
 *     PT_DO, PT_DELETE, PT_SET_SESSION_VARIABLES, PT_SELECT, PT_UPDATE
 *
 * 2) For below node types, searching is executed on its args:
 *     PT_UNION, PT_DIFFERENCE, PT_INTERSECTION
 *
 * 3) For below node types, searching is executed on its all nested nodes:
 *     PT_FUNCTION, PT_EXPR, PT_MERGE, PT_INSERT
 */
static PT_NODE *
pt_find_aggregate_analytic_in_where (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *find = NULL;

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

  switch (node->node_type)
    {
    case PT_DO:
      find = pt_find_aggregate_analytic_in_where (parser, node->info.do_.expr);
      break;

    case PT_SET_SESSION_VARIABLES:
      find = pt_find_aggregate_analytic_in_where (parser, node->info.set_variables.assignments);
      break;

    case PT_DELETE:
      find = pt_find_aggregate_analytic_in_where (parser, node->info.delete_.search_cond);
      break;

    case PT_SELECT:
      find = pt_find_aggregate_analytic_in_where (parser, node->info.query.q.select.where);
      break;

    case PT_UNION:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
      /* search in args recursively */
      find = pt_find_aggregate_analytic_in_where (parser, node->info.query.q.union_.arg1);
      if (find)
    {
      break;
    }

      find = pt_find_aggregate_analytic_in_where (parser, node->info.query.q.union_.arg2);
      break;

    case PT_FUNCTION:
      if (pt_is_aggregate_function (parser, node) || pt_is_analytic_function (parser, node))
    {
      find = node;
      break;
    }
      [[fallthrough]];

    case PT_EXPR:
    case PT_MERGE:
    case PT_INSERT:
      /* walk tree to search */
      (void) parser_walk_tree (parser, node, pt_find_aggregate_analytic_pre, &find, pt_find_aggregate_analytic_post,
                   &find);
      break;

    case PT_UPDATE:
      /* For UPDATE JOIN statements, aggregate functions or analytic functions cannot be used
       * in the SET and WHERE clauses.  Aggregate functions cannot be used in the UPDATE statement,
       * even if it is not an UPDATE JOIN statement.  Whether aggregate functions are used is checked
       * in the pt_semantic_check_local function.
       */
      if (node->info.update.spec->next != NULL)
    {
      find = pt_find_aggregate_analytic_in_where (parser, node->info.update.assignment);
      if (find != NULL)
        {
          break;
        }

      find = pt_find_aggregate_analytic_in_where (parser, node->info.update.search_cond);
    }
      break;

    default:
      /* for the rest node types, no need to search */
      break;
    }

  return find;
}

/*
 * pt_check_attribute_domain () - enforce composition hierarchy restrictions
 *      on a given list of attribute type definitions
 *   return:  none
 *   parser(in): the parser context
 *   attr_defs(in): a list of PT_ATTR_DEF nodes
 *   class_type(in): class, vclass, or proxy
 *   self(in): name of new class (for create case) or NULL (for alter case)
 *   reuse_oid(in): whether the class being created or altered is marked as
 *                  reusable OID (non-referable)
 *   stmt(in): current statement
 *
 * Note :
 * - enforce the composition hierarchy rules:
 *     1. enforce the (temporary?) restriction that no proxy may have an
 *        attribute whose type is heterogeneous set/multiset/sequence of
 *        some object and something else (homogeneous sets/sequences are OK)
 *     2. no attribute may have a domain of set(vclass), multiset(vclass)
 *        or sequence(vclass).
 *     3. an attribute of a class may NOT have a domain of a vclass or a proxy
 *        but may still have a domain of another class
 *     4. an attribute of a vclass may have a domain of a vclass or class
 *     5. an attribute of a proxy may have a domain of another proxy but not
 *        a class or vclass.
 *     6. an attribute cannot have a reusable OID class (a non-referable
 *        class) as a domain, neither directly nor as the domain of a set
 * - 'create class c (a c)' is not an error but a feature.
 */

static void
pt_check_attribute_domain (PARSER_CONTEXT * parser, PT_NODE * attr_defs, PT_MISC_TYPE class_type, const char *self,
               const bool reuse_oid, PT_NODE * stmt)
{
  PT_NODE *def, *att, *dtyp, *sdtyp;
  DB_OBJECT *cls;
  const char *att_nam, *typ_nam, *styp_nam;

  for (def = attr_defs; def != NULL && def->node_type == PT_ATTR_DEF; def = def->next)
    {
      att = def->info.attr_def.attr_name;
      att_nam = att->info.name.original;

      /* if it is an auto_increment column, check its domain */
      if (def->info.attr_def.auto_increment != NULL)
    {
      dtyp = def->data_type;
      switch (def->type_enum)
        {
        case PT_TYPE_INTEGER:
        case PT_TYPE_BIGINT:
        case PT_TYPE_SMALLINT:
          break;

        case PT_TYPE_NUMERIC:
          if (dtyp->info.data_type.dec_precision != 0)
        {
          PT_ERRORmf (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_AUTO_INCREMENT_DOMAIN,
                  att_nam);
        }
          break;

        default:
          PT_ERRORmf (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_AUTO_INCREMENT_DOMAIN,
              att_nam);
        }
    }

      /* we don't allow sets/multisets/sequences of vclasses or reusable OID classes */
      if (pt_is_set_type (def))
    {
      for (dtyp = def->data_type; dtyp != NULL; dtyp = dtyp->next)
        {
          if ((dtyp->type_enum == PT_TYPE_OBJECT) && (sdtyp = dtyp->info.data_type.entity)
          && (sdtyp->node_type == PT_NAME) && (styp_nam = sdtyp->info.name.original))
        {
          cls = db_find_class (styp_nam);
          if (cls != NULL)
            {
              if (db_is_vclass (cls) > 0)
            {
              PT_ERRORm (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_WANT_NO_VOBJ_IN_SETS);
              break;
            }
              if (sm_is_reuse_oid_class (cls))
            {
              PT_ERRORmf (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NON_REFERABLE_VIOLATION,
                      styp_nam);
              break;
            }
            }
          else if (self != NULL && intl_identifier_casecmp (self, styp_nam) == 0)
            {
              if (reuse_oid)
            {
              PT_ERRORmf (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NON_REFERABLE_VIOLATION,
                      styp_nam);
              break;
            }
            }
        }
        }
    }

      if (def->type_enum == PT_TYPE_OBJECT && def->data_type && def->data_type->node_type == PT_DATA_TYPE
      && (dtyp = def->data_type->info.data_type.entity) != NULL && dtyp->node_type == PT_NAME
      && (typ_nam = dtyp->info.name.original) != NULL)
    {
      /* typ_nam must be a class in the database */
      cls = db_find_class (typ_nam);
      if (!cls)
        {
          if (self != NULL && intl_identifier_casecmp (self, typ_nam) == 0)
        {
          if (reuse_oid)
            {
              PT_ERRORmf (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NON_REFERABLE_VIOLATION,
                  typ_nam);
            }
        }
          else
        {
          PT_ERRORmf (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_DEFINED, typ_nam);
        }
        }
      else
        {
          /* if dtyp is 'user.class' then check that 'user' owns 'class' */
          dtyp->info.name.db_object = cls;
          pt_check_user_owns_class (parser, dtyp);
          if (sm_is_reuse_oid_class (cls))
        {
          PT_ERRORmf (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NON_REFERABLE_VIOLATION,
                  typ_nam);
        }

          switch (class_type)
        {
        case PT_CLASS:
          /* an attribute of a class must be of type class */
          if (db_is_vclass (cls) > 0)
            {
              PT_ERRORmf (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CAN_NOT_BE_VCLASS, att_nam);
            }
          break;
        case PT_VCLASS:
          /* an attribute of a vclass must be of type vclass or class */
          break;
        default:
          break;
        }
        }
    }

      if (def->type_enum == PT_TYPE_ENUMERATION)
    {
      (void) pt_check_enum_data_type (parser, def->data_type);
    }

#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
      /* if a defined attr is TEXT, rewrite the definition */
      if (def->info.attr_def.attr_type == PT_NORMAL && PT_NAME_INFO_IS_FLAGED (att, PT_NAME_INFO_EXTERNAL))
    {
      if ((class_type != PT_CLASS) || (def->info.attr_def.data_default != NULL)
          || (def->info.attr_def.constrain_not_null == 1))
        {
          /* prevent vclass definition or set default */
          er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_REGU_NOT_IMPLEMENTED, 1, rel_major_release_string ());
          PT_ERROR (parser, stmt, er_msg ());
          return;
        }
      if (stmt->node_type == PT_CREATE_ENTITY)
        {
          stmt->info.create_entity.internal_stmts =
        pt_append_statements_on_add_attribute (parser, stmt->info.create_entity.internal_stmts, stmt, self,
                               att_nam, def);
          if (stmt->info.create_entity.internal_stmts == NULL)
        {
          return;
        }
        }
      else if (stmt->node_type == PT_ALTER)
        {
          PT_NODE *entity_nam;
          const char *cls_nam;

          entity_nam = stmt->info.alter.entity_name;
          cls_nam = entity_nam->info.name.original;
          stmt->info.alter.internal_stmts =
        pt_append_statements_on_add_attribute (parser, stmt->info.alter.internal_stmts, stmt, cls_nam, att_nam,
                               def);
          if (stmt->info.alter.internal_stmts == NULL)
        {
          return;
        }
        }
    }
#endif /* ENABLE_UNUSED_FUNCTION */
    }
}

/*
 * pt_check_mutable_attributes () - assert that a given list of attributes are
 *                                  indigenous to a given class
 *   return:  none
 *   parser(in): the parser context
 *   cls(in): a class object
 *   attr_defs(in): a list of attribute type definitions
 */

static void
pt_check_mutable_attributes (PARSER_CONTEXT * parser, DB_OBJECT * cls, PT_NODE * attr_defs)
{
  PT_NODE *def, *att;
  const char *att_nam, *cls_nam;
  DB_ATTRIBUTE *db_att;
  DB_OBJECT *super;

  assert (parser != NULL);

  if (!cls || (cls_nam = db_get_class_name (cls)) == NULL)
    {
      return;
    }

  for (def = attr_defs; def != NULL && def->node_type == PT_ATTR_DEF; def = def->next)
    {
      att = def->info.attr_def.attr_name;
      att_nam = att->info.name.original;
      db_att = db_get_attribute_force (cls, att_nam);
      if (!db_att)
    {
      PT_ERRORmf2 (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_ATTRIBUTE_OF, att_nam, cls_nam);
    }
      else
    {
      super = db_attribute_class (db_att);
      if (super != cls)
        PT_ERRORmf2 (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_HEIR_CANT_CHANGE_IT, att_nam,
             db_get_class_name (super));
    }
    }
}

/*
 * pt_check_alter () -  semantic check an alter statement
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   alter(in): an alter statement
 */
static void
pt_check_alter (PARSER_CONTEXT * parser, PT_NODE * alter)
{
  DB_OBJECT *db, *super;
  PT_ALTER_CODE code;
  PT_MISC_TYPE type;
  PT_NODE *name, *sup, *att, *qry, *attr;
  const char *cls_nam, *sup_nam, *att_nam;
  DB_ATTRIBUTE *db_att;
  DB_METHOD *db_mthd;
  int is_meta;
  int is_partitioned = 0, ss_partition, trigger_involved = 0;
  char keyattr[DB_MAX_IDENTIFIER_LENGTH + 1];
#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
  DB_OBJECT *dom_cls;
  char *drop_name_list = NULL;
#endif /* ENABLE_UNUSED_FUNCTION */
  bool reuse_oid = false;
  int collation_id = -1;
  bool for_update;

  /* look up the class */
  name = alter->info.alter.entity_name;
  cls_nam = name->info.name.original;

  if (alter->info.alter.code == PT_CHANGE_ATTR || alter->info.alter.code == PT_ADD_INDEX_CLAUSE)
    {
      for_update = false;
    }
  else
    {
      for_update = true;
    }

  /* We cannot change the schema of a class by using synonym names. */
  if (db_find_synonym (cls_nam) != NULL)
    {
      PT_ERRORmf (parser, name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_EXIST, cls_nam);
      return;
    }
  else
    {
      ASSERT_ERROR ();

      if (er_errid () == ER_SYNONYM_NOT_EXIST)
    {
      er_clear ();
    }
      else
    {
      return;
    }
    }

  db = pt_find_class (parser, name, for_update);
  if (!db)
    {
      PT_ERRORmf (parser, name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_EXIST, cls_nam);
      return;
    }

  if (sm_get_class_collation (db, &collation_id) != NO_ERROR)
    {
      PT_ERROR (parser, alter, er_msg ());
      return;
    }

  reuse_oid = sm_is_reuse_oid_class (db);

  /* attach object */
  name->info.name.db_object = db;
  pt_check_user_owns_class (parser, name);

  /* check that class type is what it's supposed to be */
  if (alter->info.alter.entity_type == PT_MISC_DUMMY)
    {
      alter->info.alter.entity_type = pt_get_class_type (parser, db);
    }
  else
    {
      type = alter->info.alter.entity_type;
      if ((type == PT_CLASS && db_is_class (db) <= 0) || (type == PT_VCLASS && db_is_vclass (db) <= 0))
    {
      PT_ERRORmf2 (parser, alter, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_A, cls_nam,
               pt_show_misc_type (type));
      return;
    }
    }

  type = alter->info.alter.entity_type;
  if (do_is_partitioned_subclass (&is_partitioned, cls_nam, keyattr))
    {
      PT_ERRORmf (parser, alter->info.alter.entity_name, MSGCAT_SET_PARSER_SEMANTIC,
          MSGCAT_SEMANTIC_INVALID_PARTITION_REQUEST, cls_nam);
      return;
    }

  code = alter->info.alter.code;
  switch (code)
    {
    case PT_ADD_ATTR_MTHD:
      if (type == PT_VCLASS)
    {
      for (attr = alter->info.alter.alter_clause.attr_mthd.attr_def_list; attr; attr = attr->next)
        {
          if (attr->info.attr_def.auto_increment != NULL)
        {
          PT_ERRORm (parser, alter, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_VCLASS_ATT_CANT_BE_AUTOINC);
        }
        }
    }

      for (attr = alter->info.alter.alter_clause.attr_mthd.attr_def_list; attr; attr = attr->next)
    {
      if (PT_HAS_COLLATION (attr->type_enum) && attr->node_type == PT_ATTR_DEF)
        {
          if (pt_attr_check_default_cs_coll (parser, attr, -1, collation_id) != NO_ERROR)
        {
          return;
        }
        }
    }

      pt_check_attribute_domain (parser, alter->info.alter.alter_clause.attr_mthd.attr_def_list, type, NULL, reuse_oid,
                 alter);
      for (attr = alter->info.alter.alter_clause.attr_mthd.attr_def_list; attr; attr = attr->next)
    {
      attr->info.attr_def.data_default = pt_check_data_default (parser, attr->info.attr_def.data_default);
    }
      break;

    case PT_ALTER_DEFAULT:
      for (attr = alter->info.alter.alter_clause.ch_attr_def.attr_name_list; attr; attr = attr->next)
    {
      att_nam = attr->info.name.original;
      is_meta = (attr->info.name.meta_class == PT_META_ATTR);
      db_att =
        (DB_ATTRIBUTE *) (is_meta ? db_get_class_attribute (db, att_nam) : db_get_attribute_force (db, att_nam));
      if (db_att != NULL && (db_att->auto_increment != NULL || db_att->header.name_space == ID_SHARED_ATTRIBUTE))
        {
          PT_ERRORm (parser, alter, MSGCAT_SET_PARSER_SEMANTIC,
             MSGCAT_SEMANTIC_INVALID_AUTO_INCREMENT_ON_DEFAULT_SHARED);
          return;
        }
    }
      alter->info.alter.alter_clause.ch_attr_def.data_default_list =
    pt_check_data_default (parser, alter->info.alter.alter_clause.ch_attr_def.data_default_list);

      [[fallthrough]];

    case PT_MODIFY_DEFAULT:
      pt_resolve_default_external (parser, alter);
      break;

    case PT_CHANGE_ATTR:
      {
    PT_NODE *const att_def = alter->info.alter.alter_clause.attr_mthd.attr_def_list;

    if (att_def->next != NULL || att_def->node_type != PT_ATTR_DEF)
      {
        assert (false);
        break;
      }

    if (alter->info.alter.entity_type != PT_CLASS)
      {
        PT_ERRORm (parser, alter, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ALTER_CHANGE_ONLY_TABLE);
        break;
      }

    for (attr = alter->info.alter.alter_clause.attr_mthd.attr_def_list; attr; attr = attr->next)
      {
        if (PT_HAS_COLLATION (attr->type_enum) && attr->node_type == PT_ATTR_DEF)
          {
        if (pt_attr_check_default_cs_coll (parser, attr, -1, collation_id) != NO_ERROR)
          {
            return;
          }
          }
      }

    pt_check_attribute_domain (parser, att_def, type, NULL, reuse_oid, alter);
    for (attr = alter->info.alter.alter_clause.attr_mthd.attr_def_list; attr; attr = attr->next)
      {
        attr->info.attr_def.data_default = pt_check_data_default (parser, attr->info.attr_def.data_default);
      }
      }
      break;

    case PT_MODIFY_ATTR_MTHD:
      pt_check_attribute_domain (parser, alter->info.alter.alter_clause.attr_mthd.attr_def_list, type, NULL, reuse_oid,
                 alter);
      pt_check_mutable_attributes (parser, db, alter->info.alter.alter_clause.attr_mthd.attr_def_list);
      for (attr = alter->info.alter.alter_clause.attr_mthd.attr_def_list; attr; attr = attr->next)
    {
      attr->info.attr_def.data_default = pt_check_data_default (parser, attr->info.attr_def.data_default);
    }
      break;

    case PT_RENAME_ATTR_MTHD:
      if (is_partitioned && keyattr[0] && (alter->info.alter.alter_clause.rename.element_type == PT_ATTRIBUTE))
    {
      if (!strncmp (alter->info.alter.alter_clause.rename.old_name->info.name.original, keyattr,
            DB_MAX_IDENTIFIER_LENGTH))
        {
          PT_ERRORmf (parser, alter->info.alter.alter_clause.rename.old_name, MSGCAT_SET_PARSER_SEMANTIC,
              MSGCAT_SEMANTIC_PARTITION_KEY_COLUMN, keyattr);
        }
    }
      break;

    case PT_DROP_ATTR_MTHD:
      for (att = alter->info.alter.alter_clause.attr_mthd.attr_mthd_name_list; att != NULL && att->node_type == PT_NAME;
       att = att->next)
    {
      att_nam = att->info.name.original;
      is_meta = (att->info.name.meta_class == PT_META_ATTR);
      db_att =
        (DB_ATTRIBUTE *) (is_meta ? db_get_class_attribute (db, att_nam) : db_get_attribute_force (db, att_nam));
      if (db_att)
        {
          /* an inherited attribute can not be dropped by the heir */
          super = (DB_OBJECT *) db_attribute_class (db_att);
          if (super != db)
        {
          PT_ERRORmf2 (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_HEIR_CANT_CHANGE_IT, att_nam,
                   db_get_class_name (super));
        }
          if (is_partitioned && keyattr[0])
        {
          if (!strncmp (att_nam, keyattr, DB_MAX_IDENTIFIER_LENGTH))
            {
              PT_ERRORmf (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_PARTITION_KEY_COLUMN,
                  att_nam);
            }
        }

#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
          /* if it is TEXT typed attr, collect name of the domain class */
          if (sm_has_text_domain (db_att, 0))
        {
          dom_cls = (DB_OBJECT *) db_domain_class (db_attribute_domain (db_att));
          if (drop_name_list != NULL)
            {
              drop_name_list = pt_append_string (parser, drop_name_list, ",");
            }
          drop_name_list = pt_append_string (parser, drop_name_list, db_get_class_name (dom_cls));
        }
#endif /* ENABLE_UNUSED_FUNCTION */

        }
      else
        {
          /* perhaps it's a method */
          db_mthd = (DB_METHOD *) (is_meta ? db_get_class_method (db, att_nam) : db_get_method (db, att_nam));
          if (!db_mthd)
        {
          if (!is_meta)
            {
              PT_ERRORmf2 (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_METHOD_OR_ATTR, att_nam,
                   cls_nam);
            }
          else
            {
              PT_ERRORmf2 (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_CLASS_ATTR_MTHD,
                   att_nam, cls_nam);
            }
        }
        }
    }

#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
      /* create internal statements to drop the TEXT saving classes */
      if (drop_name_list)
    {
      if ((alter->info.alter.internal_stmts =
           pt_append_statements_on_drop_attributes (parser, alter->info.alter.internal_stmts,
                            drop_name_list)) == NULL)
        {
          PT_ERRORm (parser, alter, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return;
        }
    }
#endif /* ENABLE_UNUSED_FUNCTION */
      break;

    case PT_APPLY_PARTITION:
    case PT_REMOVE_PARTITION:
    case PT_ANALYZE_PARTITION:
    case PT_DROP_PARTITION:
    case PT_ADD_PARTITION:
    case PT_ADD_HASHPARTITION:
    case PT_REORG_PARTITION:
    case PT_COALESCE_PARTITION:
    case PT_PROMOTE_PARTITION:
      if (sm_class_has_triggers (db, &trigger_involved, TR_EVENT_ALL) == NO_ERROR)
    {
      if (trigger_involved)
        {
          PT_ERRORmf (parser, alter->info.alter.entity_name, MSGCAT_SET_PARSER_SEMANTIC,
              MSGCAT_SEMANTIC_CANT_PARTITION_MNG_TRIGGERS, cls_nam);
          break;
        }
    }

      if (code == PT_APPLY_PARTITION)
    {
      if (is_partitioned)
        {
          PT_ERRORmf (parser, alter->info.alter.entity_name, MSGCAT_SET_PARSER_SEMANTIC,
              MSGCAT_SEMANTIC_ALREADY_PARTITIONED_CLASS, cls_nam);
          break;
        }
      if (alter->info.alter.alter_clause.partition.info)
        {
          pt_check_partitions (parser, alter, db);
        }
    }
      else
    {
      if (!is_partitioned)
        {
          PT_ERRORmf (parser, alter->info.alter.entity_name, MSGCAT_SET_PARSER_SEMANTIC,
              MSGCAT_SEMANTIC_IS_NOT_PARTITIONED_CLASS, cls_nam);
          break;
        }
      if (code != PT_REMOVE_PARTITION)
        {
          pt_check_alter_partition (parser, alter, db);
        }
    }
      break;

    case PT_ADD_QUERY:
    case PT_MODIFY_QUERY:
      if (type != PT_CLASS && (qry = alter->info.alter.alter_clause.query.query) != NULL)
    {
      MOP me = NULL;
      MOP owner = NULL;
      bool change_owner_flag = false;

      if (db_client_type_is_loaddb ())
        {
          const char *user_name = pt_get_qualifier_name (parser, alter->info.alter.entity_name);
          if (user_name != NULL)
        {
          me = db_get_user ();
          owner = au_find_user (user_name);
          if (owner != NULL && !ws_is_same_object (owner, me))
            {
              /* set user to owner to translate query specification. */
              AU_SET_USER (owner);
              change_owner_flag = true;
            }
        }
        }

      pt_validate_query_spec (parser, qry, db);

      if (change_owner_flag)
        {
          /* restore user */
          AU_SET_USER (me);
        }
    }
      [[fallthrough]];

    case PT_DROP_QUERY:
      if (type == PT_CLASS)
    {
      PT_ERRORmf (parser, alter, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_HAVE_NO_QUERY_SPEC, cls_nam);
    }
      break;

    case PT_RESET_QUERY:
      if (type == PT_CLASS)
    {
      /* only allow views, not classes here */
      PT_ERRORmf (parser, alter, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_HAVE_NO_QUERY_SPEC, cls_nam);
    }
      else if (db_get_subclasses (db) != NULL || db_get_superclasses (db) != NULL)
    {
      /* disallow resetting query for views that have children or parents */
      PT_ERRORmf (parser, alter, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ALTER_VIEW_IN_HIERARCHY, cls_nam);
    }
      else if ((qry = alter->info.alter.alter_clause.query.query) == NULL)
    {
      break;
    }
      else
    {
      pt_check_create_view (parser, alter);
    }
      break;

    case PT_ADD_SUPCLASS:
    case PT_DROP_SUPCLASS:
      for (sup = alter->info.alter.super.sup_class_list; sup != NULL; sup = sup->next)
    {
      sup_nam = sup->info.name.original;
      super = pt_find_class (parser, sup, true);
      if (super == NULL)
        {
          PT_ERRORmf (parser, alter, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_EXIST, sup_nam);
          break;
        }
      if (sm_partitioned_class_type (super, &ss_partition, NULL, NULL) != NO_ERROR)
        {
          PT_ERROR (parser, alter, er_msg ());
          break;
        }
      if (ss_partition == DB_PARTITION_CLASS)
        {
          PT_ERRORm (parser, alter, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_REQUEST);
          break;
        }
      if (code == PT_ADD_SUPCLASS)
        {
#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
          if (sm_has_text_domain (db_get_attributes (super), 1))
        {
          /* prevent to define it as a superclass */
          er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_REGU_NOT_IMPLEMENTED, 1, rel_major_release_string ());
          PT_ERROR (parser, alter, er_msg ());
          break;
        }
#endif /* ENABLE_UNUSED_FUNCTION */
        }
      sup->info.name.db_object = super;
      pt_check_user_owns_class (parser, sup);
      if (code == PT_DROP_SUPCLASS)
        {
          if (db_is_superclass (super, db) <= 0)
        {
          PT_ERRORmf2 (parser, sup, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_SUPERCLASS_OF, sup_nam,
                   cls_nam);
        }
        }
      else          /* PT_ADD_SUPCLASS */
        {
          switch (type)
        {
        case PT_CLASS:
          if (db_is_class (super) <= 0)
            {
              PT_ERRORmf2 (parser, sup, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NONCLASS_PARENT, cls_nam,
                   sup_nam);
            }
          break;
        case PT_VCLASS:
          if (db_is_vclass (super) <= 0)
            {
              PT_ERRORmf2 (parser, sup, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NONVCLASS_PARENT, cls_nam,
                   sup_nam);
            }
          break;
        default:
          break;
        }

          if (db_is_superclass (super, db))
        {
          PT_ERRORmf2 (parser, sup, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ALREADY_SUPERCLASS, sup_nam,
                   cls_nam);
        }
          if (db == super)
        {
          PT_ERRORmf (parser, sup, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SUPERCLASS_CYCLE, sup_nam);
        }
          if (db_is_subclass (super, db))
        {
          PT_ERRORmf2 (parser, sup, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ALREADY_SUBCLASS, sup_nam,
                   cls_nam);
        }
        }
    }
      break;
    case PT_CHANGE_OWNER:
      {
    const char *new_owner;
    MOP new_owner_mop = NULL;
    SM_CLASS *class_ = NULL;
    char new_cls_nam[DB_MAX_IDENTIFIER_LENGTH];
    new_cls_nam[0] = '\0';
    int error = NO_ERROR;

    new_owner = alter->info.alter.alter_clause.user.user_name->info.name.original;

    new_owner_mop = au_find_user (new_owner);
    if (new_owner_mop == NULL)
      {
        PT_ERRORmf (parser, name, MSGCAT_SET_ERROR, -(ER_AU_INVALID_USER), new_owner);
        break;
      }

    error = au_fetch_class_force (db, &class_, AU_FETCH_UPDATE);
    if (error != NO_ERROR)
      {
        ASSERT_ERROR_AND_SET (error);
        break;
      }

    /* To change the owner of a system class is not allowed. */
    if (sm_issystem (class_))
      {
        PT_ERRORmf (parser, name, MSGCAT_SET_ERROR, -(ER_AU_CANT_ALTER_OWNER_OF_SYSTEM_CLASS), "");
        return;
      }

    /* When changing to the same owner, a No-Operation is performed. */
    if (ws_is_same_object (class_->owner, new_owner_mop))
      {
        break;
      }

    /* When changing the owner of a class, the synonym name cannot be changed to be the same as the class name. */
    snprintf (new_cls_nam, DB_MAX_IDENTIFIER_LENGTH, "%s.%s", new_owner, sm_remove_qualifier_name (cls_nam));

    if (db_find_synonym (new_cls_nam) != NULL)
      {
        PT_ERRORmf (parser, name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_ALREADY_EXIST, new_cls_nam);
        break;
      }
    else
      {
        ASSERT_ERROR ();

        if (er_errid () == ER_SYNONYM_NOT_EXIST)
          {
        er_clear ();
          }
        else
          {
        break;
          }
      }

    /* When changing the owner of a class, the class name cannot be changed to be the same as the class name. */
    if (db_find_class (new_cls_nam) != NULL)
      {
        PT_ERRORmf (parser, name, MSGCAT_SET_ERROR, -(ER_LC_CLASSNAME_EXIST), new_cls_nam);
        break;
      }
    else
      {
        ASSERT_ERROR ();

        if (er_errid () == ER_LC_UNKNOWN_CLASSNAME)
          {
        er_clear ();
          }
        else
          {
        break;
          }
      }
      }
      break;
    default:
      break;
    }
}

/*
 * attribute_name () -  return the name of this attribute
 *   return:  printable name of att
 *   parser(in): the parser context
 *   att(in): an attribute
 */
static const char *
attribute_name (PARSER_CONTEXT * parser, PT_NODE * att)
{
  if (!att)
    {
      return NULL;
    }

  if (att->node_type == PT_ATTR_DEF)
    {
      att = att->info.attr_def.attr_name;
    }

  if (att->node_type != PT_NAME)
    {
      return NULL;
    }

  return att->info.name.original;
}

/*
 * is_shared_attribute () -  is this a shared attribute?
 *   return:  nonzero if att is a shared attribute
 *   parser(in): the parser context
 *   att(in): an attribute
 */
static int
is_shared_attribute (PARSER_CONTEXT * parser, PT_NODE * att)
{
  if (!att)
    {
      return 0;
    }

  if (att->node_type == PT_ATTR_DEF)
    {
      if (att->info.attr_def.attr_type == PT_SHARED)
    {
      return 1;
    }

      if (!(att = att->info.attr_def.attr_name))
    {
      return 0;
    }
    }

  if (att->node_type != PT_NAME)
    {
      return 0;
    }

  return (att->info.name.meta_class == PT_SHARED);
}

/*
 * pt_find_partition_column_count_func () - find the number of the name node
 *                      which can be used as the partition
 *                      column
 *   return:
 *   func(in):
 *   name_node(in/out):
 */
static int
pt_find_partition_column_count_func (PT_NODE * func, PT_NODE ** name_node)
{
  int cnt = 0, ret;
  PT_NODE *f_arg;

  if (func == NULL)
    {
      return 0;
    }

  if (func->node_type != PT_FUNCTION)
    {
      return 0;
    }

  switch (func->info.function.function_type)
    {
    case F_INSERT_SUBSTRING:
    case F_ELT:
    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:
      break;
    default:
      return 0;         /* unsupported function */
    }

  f_arg = func->info.function.arg_list;
  while (f_arg != NULL)
    {
      if (f_arg->node_type == PT_NAME)
    {
      cnt++;
      *name_node = f_arg;
    }
      else if (f_arg->node_type == PT_EXPR)
    {
      ret = pt_find_partition_column_count (f_arg, name_node);
      if (ret > 0)
        {
          cnt += ret;
        }
    }
      f_arg = f_arg->next;
    }

  return cnt;
}

/*
 * pt_find_partition_column_count () - find the number of the name node which
 *                                     can be used as the partition column
 *   return:
 *   expr(in):
 *   name_node(in/out):
 */
static int
pt_find_partition_column_count (PT_NODE * expr, PT_NODE ** name_node)
{
  int cnt = 0, ret;

  if (expr == NULL)
    {
      return 0;
    }

  if (expr->node_type != PT_EXPR)
    {
      return 0;
    }

  switch (expr->info.expr.op)
    {
    case PT_FUNCTION_HOLDER:
      assert (expr->info.expr.arg1 != NULL);
      return pt_find_partition_column_count_func (expr->info.expr.arg1, name_node);
    case PT_PLUS:
    case PT_MINUS:
    case PT_TIMES:
    case PT_DIVIDE:
    case PT_UNARY_MINUS:
    case PT_BIT_NOT:
    case PT_BIT_AND:
    case PT_BIT_OR:
    case PT_BIT_XOR:
    case PT_BITSHIFT_LEFT:
    case PT_BITSHIFT_RIGHT:
    case PT_DIV:
    case PT_MOD:
    case PT_ACOS:
    case PT_ASIN:
    case PT_ATAN:
    case PT_ATAN2:
    case PT_COS:
    case PT_SIN:
    case PT_TAN:
    case PT_COT:
    case PT_DEGREES:
    case PT_RADIANS:
    case PT_PI:
    case PT_LN:
    case PT_LOG2:
    case PT_LOG10:
    case PT_FORMAT:
    case PT_DATE_FORMAT:
    case PT_STR_TO_DATE:
    case PT_CONCAT:
    case PT_CONCAT_WS:
    case PT_FIELD:
    case PT_LEFT:
    case PT_RIGHT:
    case PT_LOCATE:
    case PT_MID:
    case PT_STRCMP:
    case PT_REVERSE:
    case PT_BIT_COUNT:
    case PT_ADDDATE:
    case PT_DATE_ADD:
    case PT_SUBDATE:
    case PT_DATE_SUB:
    case PT_DATEF:
    case PT_TIMEF:
    case PT_DATEDIFF:
    case PT_TIMEDIFF:
    case PT_MODULUS:
    case PT_POSITION:
    case PT_FINDINSET:
    case PT_SUBSTRING:
    case PT_SUBSTRING_INDEX:
    case PT_OCTET_LENGTH:
    case PT_BIT_LENGTH:
    case PT_CHAR_LENGTH:
    case PT_LOWER:
    case PT_UPPER:
    case PT_HEX:
    case PT_ASCII:
    case PT_CONV:
    case PT_BIN:
    case PT_MD5:
    case PT_TO_BASE64:
    case PT_FROM_BASE64:
    case PT_TRIM:
    case PT_LTRIM:
    case PT_RTRIM:
    case PT_LIKE_LOWER_BOUND:
    case PT_LIKE_UPPER_BOUND:
    case PT_LPAD:
    case PT_RPAD:
    case PT_REPEAT:
    case PT_SPACE:
    case PT_REPLACE:
    case PT_TRANSLATE:
    case PT_ADD_MONTHS:
    case PT_LAST_DAY:
    case PT_MONTHS_BETWEEN:
    case PT_TO_DATE:
    case PT_TO_NUMBER:
    case PT_TO_TIME:
    case PT_TO_TIMESTAMP:
    case PT_TO_DATETIME:
    case PT_SCHEMA:
    case PT_DATABASE:
    case PT_VERSION:
    case PT_TIME_FORMAT:
    case PT_TIMESTAMP:
    case PT_YEARF:
    case PT_MONTHF:
    case PT_DAYF:
    case PT_DAYOFMONTH:
    case PT_HOURF:
    case PT_MINUTEF:
    case PT_SECONDF:
    case PT_QUARTERF:
    case PT_WEEKDAY:
    case PT_DAYOFWEEK:
    case PT_DAYOFYEAR:
    case PT_TODAYS:
    case PT_FROMDAYS:
    case PT_TIMETOSEC:
    case PT_SECTOTIME:
    case PT_WEEKF:
    case PT_MAKEDATE:
    case PT_MAKETIME:
    case PT_ADDTIME:
    case PT_UNIX_TIMESTAMP:
    case PT_FROM_UNIXTIME:
    case PT_EXTRACT:
    case PT_TO_CHAR:
    case PT_CAST:
    case PT_STRCAT:
    case PT_FLOOR:
    case PT_CEIL:
    case PT_POWER:
    case PT_ROUND:
    case PT_ABS:
    case PT_LOG:
    case PT_EXP:
    case PT_SQRT:
    case PT_TRUNC:
    case PT_BIT_TO_BLOB:
    case PT_BLOB_FROM_FILE:
    case PT_BLOB_LENGTH:
    case PT_BLOB_TO_BIT:
    case PT_CHAR_TO_BLOB:
    case PT_CHAR_TO_CLOB:
    case PT_CLOB_FROM_FILE:
    case PT_CLOB_LENGTH:
    case PT_CLOB_TO_CHAR:
    case PT_TYPEOF:
    case PT_INET_ATON:
    case PT_INET_NTOA:
    case PT_DBTIMEZONE:
    case PT_SESSIONTIMEZONE:
    case PT_FROM_TZ:
    case PT_NEW_TIME:
    case PT_TO_DATETIME_TZ:
    case PT_TO_TIMESTAMP_TZ:
    case PT_CONV_TZ:
      break;
    default:
      return -1;        /* unsupported expression */
    }

  if (expr->info.expr.arg1 != NULL)
    {
      if (expr->info.expr.arg1->node_type == PT_NAME)
    {
      *name_node = expr->info.expr.arg1;
      cnt++;
    }
      else if (expr->info.expr.arg1->node_type == PT_VALUE)
    {
      if (expr->info.expr.arg1->type_enum == PT_TYPE_NULL)
        {
          return -1;
        }
    }
      else if (expr->info.expr.arg1->node_type == PT_EXPR)
    {
      ret = pt_find_partition_column_count (expr->info.expr.arg1, name_node);
      if (ret < 0)
        {
          return -1;
        }
      cnt += ret;
    }
    }

  if (expr->info.expr.arg2 != NULL)
    {
      if (expr->info.expr.arg2->node_type == PT_NAME)
    {
      *name_node = expr->info.expr.arg2;
      cnt++;
    }
      else if (expr->info.expr.arg2->node_type == PT_VALUE)
    {           /* except default NULL parameter */
      if (expr->info.expr.arg2->type_enum == PT_TYPE_NULL
          && ((expr->info.expr.arg2->line_number != expr->info.expr.arg3->line_number)
          || (expr->info.expr.arg2->column_number != expr->info.expr.arg3->column_number)))
        {
          return -1;
        }
    }
      else if (expr->info.expr.arg2->node_type == PT_EXPR)
    {
      ret = pt_find_partition_column_count (expr->info.expr.arg2, name_node);
      if (ret < 0)
        {
          return -1;
        }
      cnt += ret;
    }
    }

  if (expr->info.expr.arg3 != NULL)
    {
      if (expr->info.expr.arg3->node_type == PT_NAME)
    {
      *name_node = expr->info.expr.arg3;
      cnt++;
    }
      else if (expr->info.expr.arg3->node_type == PT_VALUE)
    {
      if (expr->info.expr.arg3->type_enum == PT_TYPE_NULL)
        {
          return -1;
        }
    }
      else if (expr->info.expr.arg3->node_type == PT_EXPR)
    {
      ret = pt_find_partition_column_count (expr->info.expr.arg3, name_node);
      if (ret < 0)
        {
          return -1;
        }
      cnt += ret;
    }
    }
  return cnt;
}

/*
 * pt_value_links_add () -
 *   return:
 *   parser(in):
 *   parts(in):
 *   val(in):
 *   ptl(in):
 */
static int
pt_value_links_add (PARSER_CONTEXT * parser, PT_NODE * parts, PT_NODE * val, PT_VALUE_LINKS * ptl)
{
  PT_VALUE_LINKS *vblk, *blks;

  vblk = (PT_VALUE_LINKS *) malloc (sizeof (PT_VALUE_LINKS));
  if (vblk == NULL)
    {
      PT_ERRORm (parser, val, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      return ER_FAILED;
    }

  vblk->vallink = val;
  vblk->next = ptl->next;
  if (ptl->next == NULL)
    {               /* first item */
      ptl->next = vblk;
      return 0;
    }

  for (blks = ptl->next; blks; blks = blks->next)
    {
      if (val == NULL)
    {
      if (blks->vallink == NULL)
        {
          /* MAXVALUE or NULL duplicate */
          goto duplicate_error;
        }
    }
      else if (blks->vallink != NULL)
    {
      if (db_value_compare (pt_value_to_db (parser, val), pt_value_to_db (parser, blks->vallink)) == DB_EQ)
        {
          goto duplicate_error;
        }
    }
    }

  ptl->next = vblk;

  return NO_ERROR;

duplicate_error:
  if (vblk != NULL)
    {
      free_and_init (vblk);
    }
  PT_ERRORmf (parser, val, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DUPLICATE_PARTITION_DEF,
          parts->info.parts.name->info.name.original);
  return ER_FAILED;
}

/*
 * pt_check_partition_values () - perform semantic check on partition
 *                range/list specification
 *   return: error code or NO_ERROR
 *   parser(in)       : parser context
 *   desired_type(in) : desired type for partition values
 *   data_type(in)    : data type for desired_type
 *   ptl(in)          : values context
 *   parts (in)       : node specifying one partition
 */
static int
pt_check_partition_values (PARSER_CONTEXT * parser, PT_TYPE_ENUM desired_type, PT_NODE * data_type,
               PT_VALUE_LINKS * ptl, PT_NODE * parts)
{
  int error = NO_ERROR;
  PT_NODE *val = NULL;
  const char *value_text = NULL;

  if (parts->info.parts.values == NULL)
    {
      /* MAXVALUE specification */
      return pt_value_links_add (parser, parts, NULL, ptl);
    }

  for (val = parts->info.parts.values; val != NULL; val = val->next)
    {
      bool has_different_collation = false;
      bool has_different_codeset = false;

      if (val->node_type != PT_VALUE)
    {
      /* Only values are allowed in partition LIST or RANGE specification. */
      assert_release (val->node_type != PT_VALUE);
      PT_ERROR (parser, val, er_msg ());
      error = ER_FAILED;
      break;
    }

      if (PT_HAS_COLLATION (val->type_enum) && data_type != NULL && PT_HAS_COLLATION (data_type->type_enum))
    {
      if ((val->data_type != NULL && data_type->info.data_type.units != val->data_type->info.data_type.units)
          || (val->data_type == NULL && data_type->info.data_type.units != LANG_SYS_CODESET))
        {
          has_different_codeset = true;
        }

      if ((val->data_type != NULL
           && data_type->info.data_type.collation_id != val->data_type->info.data_type.collation_id)
          || (val->data_type == NULL && data_type->info.data_type.collation_id != LANG_SYS_COLLATION))
        {
          has_different_collation = true;
        }
    }

      if (has_different_codeset == true)
    {
      int val_codeset;

      val_codeset = (val->data_type != NULL) ? val->data_type->info.data_type.units : LANG_SYS_CODESET;

      error = ER_FAILED;
      PT_ERRORmf2 (parser, val, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_PARTITION_VAL_CODESET,
               lang_charset_introducer ((INTL_CODESET) val_codeset),
               lang_charset_introducer ((INTL_CODESET) data_type->info.data_type.units));
      break;
    }

      if (val->type_enum != PT_TYPE_NULL && (val->type_enum != desired_type || has_different_collation == true))
    {
      /* Coerce this value to the desired type. We have to preserve the original text of the value for replication
       * reasons. The coercion below will either be successful or fail, but it should not alter the way in which
       * the original statement is printed */
      value_text = val->info.value.text;
      val->info.value.text = NULL;
      error = pt_coerce_value (parser, val, val, desired_type, data_type);
      val->info.value.text = value_text;
      if (error != NO_ERROR)
        {
          break;
        }
    }

      /* add this value to the values list */
      if (val->type_enum == PT_TYPE_NULL)
    {
      error = pt_value_links_add (parser, parts, NULL, ptl);
    }
      else
    {
      error = pt_value_links_add (parser, parts, val, ptl);
    }

      if (error != NO_ERROR)
    {
      break;
    }
    }

  return error;
}


/*
 * pt_check_partitions () - do semantic checks on a partition clause
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   stmt(in): a create class or alter class statement
 *   dbobj(in):
 *
 * Note :
 * check that
 * - stmt's expression have an attribute
 * - expression's attribute is not set nor object type
 * - partition type is equals to partition definitions
 * - partition max
 * - valid hash size
 */

static void
pt_check_partitions (PARSER_CONTEXT * parser, PT_NODE * stmt, MOP dbobj)
{
  PT_NODE *pinfo, *pcol, *attr, *pattr, *parts;
  int name_count, valchk, parts_cnt;
  PT_VALUE_LINKS vlinks = { NULL, NULL };
  PT_VALUE_LINKS *pvl, *delpvl;
  SEMANTIC_CHK_INFO sc_info = { NULL, NULL, 0, 0, 0, false, false };
  PT_NODE *expr_type;
  SM_CLASS *smclass;
  SM_ATTRIBUTE *smatt;
  bool chkflag = false;
  DB_QUERY_TYPE *query_columns = NULL, *column = NULL;
  int error = NO_ERROR;

  assert (parser != NULL);

  if (!stmt || (stmt->node_type != PT_CREATE_ENTITY && stmt->node_type != PT_ALTER))
    {
      return;
    }

  if (stmt->node_type == PT_CREATE_ENTITY)
    {
      pinfo = stmt->info.create_entity.partition_info;
    }
  else
    {
      pinfo = stmt->info.alter.alter_clause.partition.info;
    }

  if (pinfo == NULL || pinfo->node_type != PT_PARTITION || pinfo->info.partition.type > PT_PARTITION_LIST)
    {
      PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_DEFINITION);
      return;
    }

  if (0 < parser->host_var_count)
    {
      PT_ERRORm (parser, pinfo, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_HOSTVAR_IN_DDL);
      return;
    }

  pcol = pinfo->info.partition.expr;
  if (pcol->node_type != PT_NAME && pcol->node_type != PT_EXPR)
    {
      PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NO_PARTITION_COLUMN);
      return;
    }

  if (pcol->node_type == PT_EXPR)
    {
      name_count = pt_find_partition_column_count (pcol, &pcol);
      if (name_count < 0)
    {           /* NULL constant exist */
      PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_DEFINITION);
      return;
    }
      else if (name_count == 0)
    {
      PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NO_PARTITION_COLUMN);
      return;
    }
      else if (name_count > 1)
    {
      PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ONLYONE_PARTITION_COLUMN);
      return;
    }
    }

  assert (pcol->node_type == PT_NAME);

  if (stmt->node_type == PT_CREATE_ENTITY)
    {
      for (attr = stmt->info.create_entity.attr_def_list, chkflag = false; attr && attr->node_type == PT_ATTR_DEF;
       attr = attr->next)
    {
      pattr = attr->info.attr_def.attr_name;
      if (pattr == NULL)
        {
          continue;
        }

      if (!intl_identifier_casecmp (pcol->info.name.original, pattr->info.name.original))
        {
          if (attr->info.attr_def.attr_type != PT_NORMAL)
        {
          PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_COLUMN_TYPE);
          return;
        }
          pcol->type_enum = attr->type_enum;
          if (attr->data_type != NULL)
        {
          assert (pcol->data_type == NULL);
          pcol->data_type = parser_copy_tree (parser, attr->data_type);
        }
          else
        {
          TP_DOMAIN *d;

          d = pt_type_enum_to_db_domain (pcol->type_enum);
          d = tp_domain_cache (d);
          pcol->data_type = pt_domain_to_data_type (parser, d);
        }
          pinfo->info.partition.keycol = parser_copy_tree (parser, pcol);
          chkflag = true;
          break;
        }
    }

      /* check if partitioning is requested by a column in SELECT query */
      if (!chkflag && stmt->info.create_entity.create_select != NULL)
    {
      int error = NO_ERROR;
      PT_NODE *qry_select = stmt->info.create_entity.create_select;
      /* get columns from SELECT result */

      error = pt_get_select_query_columns (parser, qry_select, &query_columns);
      if (error != NO_ERROR)
        {
          /* error message already set at the above compilation step */
          return;
        }

      for (column = query_columns; column != NULL; column = db_query_format_next (column))
        {
          if (!intl_identifier_casecmp (column->original_name, pcol->info.name.original))
        {
          pcol->type_enum = pt_db_to_type_enum (column->db_type);

          assert (column->domain != NULL);
          pcol->data_type = pt_domain_to_data_type (parser, column->domain);
          pinfo->info.partition.keycol = parser_copy_tree (parser, pcol);
          chkflag = true;
          break;
        }
        }
      if (query_columns != NULL)
        {
          db_free_query_format (query_columns);
          query_columns = NULL;
        }
      assert (NULL == query_columns);
    }

    }
  else
    {
      if (au_fetch_class (dbobj, &smclass, AU_FETCH_READ, AU_SELECT) == NO_ERROR)
    {
      for (smatt = smclass->attributes; smatt != NULL; smatt = (SM_ATTRIBUTE *) smatt->header.next)
        {
          if (SM_COMPARE_NAMES (smatt->header.name, pcol->info.name.original) == 0)
        {
          if (smatt->class_mop != stmt->info.alter.entity_name->info.name.db_object)
            {
              PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC,
                 MSGCAT_SEMANTIC_INVALID_PARTITION_INHERITED_ATTR);
            }
          pcol->type_enum = pt_db_to_type_enum (smatt->type->id);
          assert (smatt->domain != NULL);
          pcol->data_type = pt_domain_to_data_type (parser, smatt->domain);
          pinfo->info.partition.keycol = parser_copy_tree (parser, pcol);
          chkflag = true;
          break;
        }
        }
    }
    }

  if (chkflag)
    {
      switch (pcol->type_enum)
    {
    case PT_TYPE_INTEGER:
    case PT_TYPE_BIGINT:
    case PT_TYPE_SMALLINT:
    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_CHAR:
    case PT_TYPE_VARCHAR:
      break;
    default:
      PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_COLUMN_TYPE);
      return;
    }
    }
  else
    {
      bool found = false;

      if (stmt->node_type == PT_CREATE_ENTITY && stmt->info.create_entity.supclass_list)
    {
      DB_OBJECT *sup_dbobj = stmt->info.create_entity.supclass_list->info.name.db_object;

      if (au_fetch_class (sup_dbobj, &smclass, AU_FETCH_READ, AU_SELECT) == NO_ERROR)
        {
          for (smatt = smclass->attributes; smatt != NULL; smatt = (SM_ATTRIBUTE *) smatt->header.next)
        {
          if (SM_COMPARE_NAMES (smatt->header.name, pcol->info.name.original) == 0)
            {
              PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC,
                 MSGCAT_SEMANTIC_INVALID_PARTITION_INHERITED_ATTR);
              found = true;
            }
        }
        }
    }

      if (!found)
    {
      PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NO_PARTITION_COLUMN);
    }
    }

  pcol = pinfo->info.partition.expr;
  sc_info.top_node = pcol;
  sc_info.donot_fold = false;
  expr_type = pt_semantic_type (parser, pcol, &sc_info);
  if (expr_type)
    {
      switch (expr_type->type_enum)
    {
    case PT_TYPE_INTEGER:
    case PT_TYPE_BIGINT:
    case PT_TYPE_SMALLINT:
    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_CHAR:
    case PT_TYPE_VARCHAR:
      break;
    default:
      PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_COLUMN_TYPE);
      return;
    }
    }
  else
    {
      PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_DEFINITION);
      return;
    }

  if (pinfo->info.partition.type == PT_PARTITION_HASH)
    {
      PT_NODE *hashsize_nodep;

      hashsize_nodep = pinfo->info.partition.hashsize;
      if (hashsize_nodep == NULL || hashsize_nodep->type_enum != PT_TYPE_INTEGER
      || hashsize_nodep->info.value.data_value.i < 1 || hashsize_nodep->info.value.data_value.i > MAX_PARTITIONS)
    {
      PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_SIZE);
    }
    }
  else
    {               /* RANGE or LIST */
      parts = pinfo->info.partition.parts;
      if (parts == NULL)
    {
      PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_DEFINITION);
      return;
    }

      parts_cnt = 0;
      for (chkflag = false; parts && parts->node_type == PT_PARTS; parts = parts->next)
    {
      PT_NODE *fpart;

      if (parts->info.parts.type != pinfo->info.partition.type)
        {
          chkflag = true;
          break;
        }
      if (parts->info.parts.values != NULL)
        {
          parts->info.parts.values =
        parser_walk_tree (parser, parts->info.parts.values, pt_check_and_replace_hostvar, &valchk, NULL, NULL);
          if ((pinfo->info.partition.type == PT_PARTITION_RANGE)
          && parts->info.parts.values->type_enum == PT_TYPE_NULL)
        {
          PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CONSTANT_TYPE_MISMATCH,
                  parts->info.parts.name->info.name.original);

          goto pvl_free_end;
        }
        }

      valchk = pt_check_partition_values (parser, expr_type->type_enum, expr_type->data_type, &vlinks, parts);
      if (valchk != NO_ERROR)
        {
          goto pvl_free_end;
        }

      for (fpart = parts->next; fpart && fpart->node_type == PT_PARTS; fpart = fpart->next)
        {
          if (!intl_identifier_casecmp (parts->info.parts.name->info.name.original,
                        fpart->info.parts.name->info.name.original))
        {
          PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DUPLICATE_PARTITION_DEF,
                  fpart->info.parts.name->info.name.original);

          goto pvl_free_end;
        }
        }

      /* check value increasing for range partition */
      if (pinfo->info.partition.type == PT_PARTITION_RANGE)
        {
          error = pt_check_range_partition_strict_increasing (parser, stmt, parts, parts->next, NULL);
          if (error != NO_ERROR)
        {
          assert (pt_has_error (parser));

          goto pvl_free_end;
        }
        }

      parts_cnt++;
    }

      if (parts_cnt > MAX_PARTITIONS)
    {
      PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_SIZE);
    }
      else if (chkflag)
    {
      PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_DEFINITION);
    }
    }

pvl_free_end:

  pvl = vlinks.next;
  while (pvl)
    {
      delpvl = pvl;
      pvl = pvl->next;
      free_and_init (delpvl);
    }
}

/*
 * partition_range_min_max () - find min/max value
 *   return:  0-process, 1-duplicate
 *   dest(in/out): min or max value
 *   inval(in): input value
 *   min_max(in): RANGE_MIN, RANGE_MAX
 *
 * Note :
 * check that
 * - stmt's expression have an attribute
 * - expression's attribute is not set nor object type
 * - partition type is equals to partition definitions
 * - partition max
 * - valid hash size
 */
static int
partition_range_min_max (DB_VALUE ** dest, DB_VALUE * inval, int min_max)
{
  int op, rst;
  DB_VALUE nullval;

  if (dest == NULL)
    {
      return 0;
    }

  if (inval == NULL)
    {
      db_make_null (&nullval);
      inval = &nullval;
    }

  if (DB_IS_NULL (inval))
    {               /* low or high infinite */
      if (*dest != NULL)
    {
      if (DB_IS_NULL (*dest))
        {
          return 1;
        }
      pr_free_ext_value (*dest);
    }
      *dest = db_value_copy (inval);
      return 0;
    }

  if (*dest == NULL)
    {
      *dest = db_value_copy (inval);
    }
  else
    {
      if (DB_IS_NULL (*dest))
    {           /* low or high infinite */
      if (DB_IS_NULL (inval))
        {
          return 1;
        }
      else
        {
          return 0;
        }
    }
      op = (min_max == RANGE_MIN) ? DB_GT : DB_LT;
      rst = db_value_compare (*dest, inval);
      if (rst == op)
    {
      pr_free_ext_value (*dest);
      *dest = db_value_copy (inval);
    }
      else if (rst == DB_EQ)
    {
      return 1;
    }
    }

  return 0;
}

/*
 * db_value_list_add () -
 *   return:
 *   ptail(out):
 *   val(in):
 */
static int
db_value_list_add (DB_VALUE_PLIST ** ptail, DB_VALUE * val)
{
  DB_VALUE_PLIST *tmp_vallist;
  DB_VALUE nullval, *chkval;

  if (ptail == NULL)
    {
      return -1;
    }

  if (val == NULL)
    {
      db_make_null (&nullval);
      chkval = &nullval;
    }
  else
    {
      chkval = val;
    }

  tmp_vallist = (DB_VALUE_PLIST *) malloc (sizeof (DB_VALUE_PLIST));
  if (tmp_vallist == NULL)
    {
      return -1;
    }

  if (*ptail == NULL)
    {
      *ptail = tmp_vallist;
    }
  else
    {
      (*ptail)->next = tmp_vallist;
      *ptail = tmp_vallist;
    }

  (*ptail)->next = NULL;
  (*ptail)->val = db_value_copy (chkval);

  return 0;
}

/*
 * db_value_list_find () -
 *   return:
 *   phead(in):
 *   val(in):
 */
static int
db_value_list_find (const DB_VALUE_PLIST * phead, const DB_VALUE * val)
{
  DB_VALUE_PLIST *tmp;
  DB_VALUE nullval, *chkval;

  if (phead == NULL)
    {
      return 0;
    }

  if (val == NULL)
    {
      db_make_null (&nullval);
      chkval = &nullval;
    }
  else
    {
      chkval = (DB_VALUE *) val;
    }

  for (tmp = (DB_VALUE_PLIST *) phead; tmp; tmp = tmp->next)
    {
      if ((DB_IS_NULL (tmp->val) && DB_IS_NULL (chkval)) || db_value_compare (tmp->val, chkval) == DB_EQ)
    {
      return 1;
    }
    }

  return 0;
}

/*
 * db_value_list_finddel () -
 *   return:
 *   phead(in/out):
 *   val(in):
 */
static int
db_value_list_finddel (DB_VALUE_PLIST ** phead, DB_VALUE * val)
{
  DB_VALUE_PLIST *tmp, *pre = NULL;
  DB_VALUE nullval, *chkval;

  if (phead == NULL)
    {
      return 0;
    }

  if (val == NULL)
    {
      db_make_null (&nullval);
      chkval = &nullval;
    }
  else
    {
      chkval = val;
    }

  for (tmp = *phead; tmp; tmp = tmp->next)
    {
      if ((DB_IS_NULL (tmp->val) && DB_IS_NULL (chkval)) || db_value_compare (tmp->val, chkval) == DB_EQ)
    {
      if (pre == NULL)
        {
          *phead = tmp->next;
        }
      else
        {
          pre->next = tmp->next;
        }

      pr_free_ext_value (tmp->val);
      free_and_init (tmp);

      return 1;
    }
      pre = tmp;
    }

  return 0;
}

/*
 * pt_check_alter_partition () - do semantic checks on a alter partition clause
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   stmt(in): a alter class statement
 *   dbobj(in):
 *
 * Note :
 * check that
 * - partition type is equals to original partition definitions
 * - partition min/max
 */

static void
pt_check_alter_partition (PARSER_CONTEXT * parser, PT_NODE * stmt, MOP dbobj)
{
  PT_NODE *name_list, *part_list;
  PT_NODE *names, *parts, *val, *next_parts;
  PT_ALTER_CODE cmd;
  SM_CLASS *smclass, *subcls;
  DB_OBJLIST *objs;
  int i, setsize;
  int orig_cnt = 0, name_cnt = 0, parts_cnt = 0, chkflag = 0;
  DB_VALUE *psize;
  char *class_name, *part_name;
  DB_VALUE minele, maxele, *minval = NULL, *maxval = NULL;
  DB_VALUE *parts_val, *partmin = NULL, *partmax = NULL;
  DB_VALUE null_val;
  DB_VALUE_PLIST *minmax_head = NULL, *minmax_tail = NULL;
  DB_VALUE_PLIST *outlist_head = NULL, *outlist_tail = NULL;
  DB_VALUE_PLIST *inlist_head = NULL, *inlist_tail = NULL;
  DB_VALUE_PLIST *min_list, *max_list;
  DB_VALUE_PLIST *p, *next;
  PT_NODE *column_dt = NULL;
  PARSER_CONTEXT *tmp_parser = NULL;
  PT_NODE **statements = NULL;
  const char *expr_sql = NULL;
  int error = NO_ERROR;

  assert (parser != NULL);

  if (stmt == NULL)
    {
      return;
    }

  db_make_null (&minele);
  db_make_null (&maxele);
  db_make_null (&null_val);

  class_name = (char *) stmt->info.alter.entity_name->info.name.original;
  cmd = stmt->info.alter.code;
  if (cmd == PT_DROP_PARTITION || cmd == PT_ANALYZE_PARTITION || cmd == PT_REORG_PARTITION
      || cmd == PT_PROMOTE_PARTITION)
    {
      name_list = stmt->info.alter.alter_clause.partition.name_list;
    }
  else
    {
      name_list = NULL;
    }

  if (0 < parser->host_var_count)
    {
      PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_HOSTVAR_IN_DDL);
      return;
    }

  if (cmd == PT_ADD_HASHPARTITION || cmd == PT_COALESCE_PARTITION)
    {
      psize = pt_value_to_db (parser, stmt->info.alter.alter_clause.partition.size);
    }
  else
    {
      psize = NULL;
    }

  if (cmd == PT_ADD_PARTITION || cmd == PT_REORG_PARTITION)
    {
      part_list = stmt->info.alter.alter_clause.partition.parts;
    }
  else
    {
      part_list = NULL;
    }

  switch (cmd)
    {               /* parameter check */
    case PT_DROP_PARTITION: /* name_list */
    case PT_PROMOTE_PARTITION:
      if (name_list == NULL)
    {
      chkflag = 1;
    }
      break;
    case PT_ANALYZE_PARTITION:  /* NULL = ALL */
      break;
    case PT_ADD_PARTITION:  /* parts */
      if (part_list == NULL)
    {
      chkflag = 1;
    }
      break;
    case PT_ADD_HASHPARTITION:  /* psize */
    case PT_COALESCE_PARTITION: /* psize */
      if (psize == NULL)
    {
      chkflag = 1;
    }
      break;
    case PT_REORG_PARTITION:    /* name_list, parts */
      if (name_list == NULL || part_list == NULL)
    {
      chkflag = 1;
    }
      break;
    default:
      chkflag = 1;
      break;
    }

  if (chkflag)
    {
      PT_ERRORmf (parser, stmt->info.alter.entity_name, MSGCAT_SET_PARSER_SEMANTIC,
          MSGCAT_SEMANTIC_INVALID_PARTITION_REQUEST, class_name);
      return;
    }

  /* get partition information : count, name, type */
  if (au_fetch_class (dbobj, &smclass, AU_FETCH_READ, AU_SELECT) != NO_ERROR || smclass->partition == NULL)
    {
      PT_ERRORmf (parser, stmt->info.alter.entity_name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SELECT_AUTH_FAILURE,
          class_name);
      return;
    }

  if (smclass->partition->partition_type != PT_PARTITION_HASH)
    {
      PT_NODE *select_list;

      tmp_parser = parser_create_parser ();
      if (tmp_parser == NULL)
    {
      assert (er_errid () != NO_ERROR);

      goto check_end;
    }

      /* get partition expr */
      expr_sql = smclass->partition->expr;

      /* compile the select statement and get the correct data_type */
      statements = parser_parse_string (tmp_parser, expr_sql);
      if (statements == NULL)
    {
      if (er_errid () == NO_ERROR)
        {
          PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_INFO);
        }

      goto check_end;
    }

      statements[0] = pt_compile (tmp_parser, statements[0]);
      if (statements[0] == NULL)
    {
      if (er_errid () == NO_ERROR)
        {
          PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_INFO);
        }

      goto check_end;
    }

      assert (statements[0]->node_type == PT_SELECT && statements[0]->info.query.q.select.list != NULL);

      select_list = statements[0]->info.query.q.select.list;
      if (select_list->data_type != NULL)
    {
      column_dt = parser_copy_tree (parser, select_list->data_type);
    }
      else
    {
      column_dt = pt_domain_to_data_type (parser,
                          tp_domain_resolve_default (pt_type_enum_to_db (select_list->type_enum)));
    }

      parser_free_parser (tmp_parser);
      tmp_parser = NULL;
    }

  chkflag = 0;
  switch (cmd)
    {               /* possible action check */
    case PT_DROP_PARTITION: /* RANGE/LIST */
    case PT_ADD_PARTITION:
    case PT_REORG_PARTITION:
    case PT_PROMOTE_PARTITION:
      if (smclass->partition->partition_type == PT_PARTITION_HASH)
    {
      chkflag = 1;
    }
      break;

    case PT_ANALYZE_PARTITION:  /* ALL */
      break;

    case PT_ADD_HASHPARTITION:  /* HASH */
    case PT_COALESCE_PARTITION:
      if (smclass->partition->partition_type != PT_PARTITION_HASH)
    {
      chkflag = 1;
    }
      break;

    default:
      break;
    }

  if (chkflag)
    {
      PT_ERRORmf (parser, stmt->info.alter.entity_name, MSGCAT_SET_PARSER_SEMANTIC,
          MSGCAT_SEMANTIC_INVALID_PARTITION_REQUEST, class_name);
      goto check_end;
    }

  db_make_null (&null_val);
  for (objs = smclass->users; objs; objs = objs->next)
    {
      if (au_fetch_class (objs->op, &subcls, AU_FETCH_READ, AU_SELECT) != NO_ERROR)
    {
      PT_ERRORc (parser, stmt, er_msg ());
      goto check_end;
    }

      if (!subcls->partition)
    {
      continue;     /* not partitioned */
    }

      orig_cnt++;

      db_make_null (&minele);
      db_make_null (&maxele);

      if (psize == NULL)
    {           /* RANGE or LIST */
      if (subcls->partition == NULL || subcls->partition->pname == NULL)
        {
          /* get partition type */
          goto invalid_partition_info_fail;
        }

      if (smclass->partition->partition_type == DB_PARTITION_RANGE && cmd != PT_DROP_PARTITION
          && cmd != PT_PROMOTE_PARTITION)
        {
          if (set_get_element_nocopy (subcls->partition->values, 0, &minele) != NO_ERROR)
        {
          goto invalid_partition_info_fail; /* RANGE MIN */
        }
          if (set_get_element_nocopy (subcls->partition->values, 1, &maxele) != NO_ERROR)
        {
          goto invalid_partition_info_fail; /* RANGE MAX */
        }

          /* MAX VALUE find for ADD PARTITION */
          if (cmd == PT_ADD_PARTITION)
        {
          partition_range_min_max (&maxval, &maxele, RANGE_MAX);
        }
        }

      for (names = name_list, chkflag = 0; names; names = names->next)
        {
          if (!names->flag.partition_pruned
          && !intl_identifier_casecmp (names->info.name.original, subcls->partition->pname))
        {
          chkflag = 1;
          names->flag.partition_pruned = 1; /* existence marking */
          names->info.name.db_object = objs->op;

          if (smclass->partition->partition_type == DB_PARTITION_RANGE && cmd == PT_REORG_PARTITION)
            {
              partition_range_min_max (&maxval, &maxele, RANGE_MAX);
              partition_range_min_max (&minval, &minele, RANGE_MIN);
            }

          if (smclass->partition->partition_type == DB_PARTITION_LIST && cmd == PT_REORG_PARTITION)
            {
              setsize = set_size (subcls->partition->values);
              for (i = 0; i < setsize; i++)
            {   /* in-list old value */
              if (set_get_element_nocopy (subcls->partition->values, i, &minele) != NO_ERROR)
                {
                  goto invalid_partition_info_fail;
                }

              if (db_value_list_add (&inlist_tail, &minele))
                {
                  goto out_of_mem_fail;
                }

              if (inlist_head == NULL)
                {
                  inlist_head = inlist_tail;
                }
            }
            }
        }
        }

      if (chkflag == 0)
        {
          if (smclass->partition->partition_type == DB_PARTITION_LIST)
        {
          setsize = set_size (subcls->partition->values);
          for (i = 0; i < setsize; i++)
            {       /* out-list value */
              if (set_get_element_nocopy (subcls->partition->values, i, &minele) != NO_ERROR)
            {
              goto invalid_partition_info_fail;
            }

              if (db_value_list_add (&outlist_tail, &minele))
            {
              goto out_of_mem_fail;
            }

              if (outlist_head == NULL)
            {
              outlist_head = outlist_tail;
            }
            }
        }
          else if (smclass->partition->partition_type == DB_PARTITION_RANGE && cmd == PT_REORG_PARTITION)
        {
          /* for non-continuous or overlap ranges check */
          if (db_value_list_add (&minmax_tail, &minele))
            {
              goto out_of_mem_fail;
            }

          if (minmax_head == NULL)
            {
              minmax_head = minmax_tail;
            }

          if (db_value_list_add (&minmax_tail, &maxele))
            {
              goto out_of_mem_fail;
            }
        }
        }


      for (parts = part_list; parts; parts = parts->next)
        {
          if (!parts->flag.partition_pruned
          && !intl_identifier_casecmp (parts->info.parts.name->info.name.original, subcls->partition->pname))
        {
          parts->flag.partition_pruned = 1; /* existence marking */
        }
        }
    }
    }

  if (name_list)
    {               /* checks unknown partition */
      for (names = name_list; names; names = names->next)
    {
      if (!names->flag.partition_pruned)
        {
          PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_PARTITION_DOES_NOT_EXIST,
              names->info.name.original);
          goto check_end;
        }
      name_cnt++;
    }
    }

  if (part_list)
    {               /* checks duplicated definition */
      for (parts = part_list; parts; parts = parts->next)
    {
      if (smclass->partition->partition_type != (int) parts->info.parts.type)
        {
          PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_PARTITION_TYPE_MISMATCH,
              parts->info.parts.name->info.name.original);
          goto check_end;
        }

      /* next part */
      next_parts = parts->next;
      if (next_parts != NULL && smclass->partition->partition_type != (int) next_parts->info.parts.type)
        {
          PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_PARTITION_TYPE_MISMATCH,
              next_parts->info.parts.name->info.name.original);
          goto check_end;
        }

      part_name = (char *) parts->info.parts.name->info.name.original;
      if (parts->info.parts.values)
        {
          parts->info.parts.values =
        parser_walk_tree (parser, parts->info.parts.values, pt_check_and_replace_hostvar, &chkflag, NULL, NULL);
          if (parts->info.parts.type == PT_PARTITION_RANGE && parts->info.parts.values->type_enum == PT_TYPE_NULL)
        {
          PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CONSTANT_TYPE_MISMATCH,
                  part_name);
          goto check_end;
        }
        }

      if (smclass->partition->partition_type == PT_PARTITION_RANGE)
        {
          /* corece the partition value */
          val = parts->info.parts.values;

          if (val != NULL)
        {
          error = pt_coerce_partition_value_with_data_type (parser, val, column_dt);
          if (error != NO_ERROR)
            {
              error = MSGCAT_SEMANTIC_CONSTANT_TYPE_MISMATCH;

              PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, error, part_name);

              goto check_end;
            }
        }

          /* check strict increasing rule */
          error = pt_check_range_partition_strict_increasing (parser, stmt, parts, next_parts, column_dt);
          if (error != NO_ERROR)
        {
          assert (pt_has_error (parser));

          goto check_end;
        }

          parts_val = pt_value_to_db (parser, parts->info.parts.values);
          if (parts_val == NULL)
        {
          parts_val = &null_val;
        }

          if (db_value_list_find (inlist_head, parts_val))
        {
          PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DUPLICATE_PARTITION_DEF,
                  part_name);
          goto check_end;
        }

          if (db_value_list_add (&inlist_tail, parts_val))
        {
          PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          goto check_end;
        }
          if (inlist_head == NULL)
        {
          inlist_head = inlist_tail;
        }

          partition_range_min_max (&partmax, parts_val, RANGE_MAX);
          if (!DB_IS_NULL (parts_val))
        {       /* MAXVALUE */
          partition_range_min_max (&partmin, parts_val, RANGE_MIN);
        }
        }
      else
        {           /* LIST */
          for (val = parts->info.parts.values; val && val->node_type == PT_VALUE; val = val->next)
        {
          error = pt_coerce_partition_value_with_data_type (parser, val, column_dt);
          if (error != NO_ERROR)
            {
              PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CONSTANT_TYPE_MISMATCH,
                  part_name);

              goto check_end;
            }

          parts_val = pt_value_to_db (parser, val);
          if (parts_val == NULL)
            {
              parts_val = &null_val;
            }

          /* new-list duplicate check */
          if (db_value_list_find (minmax_head, parts_val))
            {
              PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DUPLICATE_PARTITION_DEF,
                  part_name);
              goto check_end;
            }

          if (db_value_list_add (&minmax_tail, parts_val))
            {
              PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
              goto check_end;
            }
          if (minmax_head == NULL)
            {
              minmax_head = minmax_tail;
            }

          /* out-list duplicate check */
          if (db_value_list_find (outlist_head, parts_val))
            {
              PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DUPLICATE_PARTITION_DEF,
                  part_name);
              goto check_end;
            }

          /* in-list delete - lost check */
          db_value_list_finddel (&inlist_head, parts_val);
        }
        }

      /* check if has duplicated name in parts_list of itself */
      for (next_parts = parts->next; next_parts; next_parts = next_parts->next)
        {
          if (!intl_identifier_casecmp (next_parts->info.parts.name->info.name.original, part_name))
        {
          PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DUPLICATE_PARTITION_DEF,
                  part_name);
          goto check_end;
        }
        }

      parts_cnt++;
      if (parts->flag.partition_pruned)
        {
          if (name_list)
        {
          for (names = name_list; names; names = names->next)
            {
              if (!intl_identifier_casecmp (part_name, names->info.name.original))
            {
              names->flag.partition_pruned = 0;
              break;    /* REORG partition name reuse */
            }
            }
          if (names != NULL)
            {
              continue;
            }
        }

          PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DUPLICATE_PARTITION_DEF, part_name);
          goto check_end;
        }
    }
    }

  if (psize == NULL)
    {               /* RANGE or LIST */
      orig_cnt = orig_cnt - name_cnt + parts_cnt;
      if (cmd != PT_PROMOTE_PARTITION)
    {
      if (orig_cnt < 1 && cmd == PT_DROP_PARTITION)
        {
          PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CANNOT_DROP_ALL_PARTITIONS);
          goto check_end;
        }

      if (orig_cnt < 1 || orig_cnt > MAX_PARTITIONS)
        {
          PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_SIZE);
          goto check_end;
        }
    }

      if (smclass->partition->partition_type == PT_PARTITION_RANGE)
    {
      if (cmd == PT_ADD_PARTITION)
        {
          if (DB_IS_NULL (maxval) || (partmin != NULL && db_value_compare (maxval, partmin) != DB_LT))
        {
          PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_END_OF_PARTITION);
          goto check_end;
        }

          /* maxval save for do_create_partition */
          stmt->info.alter.alter_clause.partition.info = pt_dbval_to_value (parser, maxval);
        }
      else if (cmd == PT_REORG_PARTITION)
        {
          if (partmin == NULL)
        {
          /* reorganizing into one partition with MAXVALUE */
          if (parts_cnt != 1)
            {
              PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_PARTITION_RANGE_INVALID,
                  class_name);
              goto check_end;
            }
        }
          else if (!DB_IS_NULL (minval) && db_value_compare (partmin, minval) != DB_GT)
        {
        range_invalid_error:
          PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_PARTITION_RANGE_INVALID,
                  class_name);
          goto check_end;
        }
          if ((DB_IS_NULL (maxval) && !DB_IS_NULL (partmax))
          || (!DB_IS_NULL (maxval) && !DB_IS_NULL (partmax) && db_value_compare (maxval, partmax) == DB_GT))
        {
          PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DATA_LOSS_IS_NOT_ALLOWED);
          goto check_end;
        }

          /* checks non-continuous or overlap ranges */
          for (min_list = minmax_head, max_list = (min_list) ? min_list->next : NULL; (min_list && max_list);
           min_list = max_list->next, max_list = (min_list) ? min_list->next : NULL)
        {
          if (DB_IS_NULL (partmax))
            {       /* new-high infinite */
              if (DB_IS_NULL (max_list->val))
            {
              goto range_invalid_error;
            }
              continue;
            }

          if (DB_IS_NULL (min_list->val))
            {       /* orig-low infinite */
              if (db_value_compare (partmin, max_list->val) != DB_GT)
            {
              goto range_invalid_error;
            }
              continue;
            }

          if (DB_IS_NULL (max_list->val))
            {       /* orig-high infinite */
              if (db_value_compare (min_list->val, partmax) == DB_LT)
            {
              goto range_invalid_error;
            }
              continue;
            }

          if ((db_value_compare (minval, min_list->val) != DB_GT
               && db_value_compare (min_list->val, partmax) == DB_LT)
              || (db_value_compare (minval, max_list->val) == DB_LT
              && db_value_compare (max_list->val, partmax) != DB_GT)
              || (db_value_compare (min_list->val, minval) != DB_GT
              && db_value_compare (minval, max_list->val) == DB_LT)
              || (db_value_compare (min_list->val, partmax) == DB_LT
              && db_value_compare (partmax, max_list->val) != DB_GT))
            {
              goto range_invalid_error;
            }
        }
        }
    }           /* end RANGE */
      else
    {           /* LIST */
      if (cmd == PT_REORG_PARTITION && inlist_head != NULL)
        {
          PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DATA_LOSS_IS_NOT_ALLOWED);
          goto check_end;
        }
    }
    }
  else
    {               /* HASH */
      if (cmd == PT_ADD_HASHPARTITION)
    {
      orig_cnt += psize->data.i;
    }
      else
    {
      orig_cnt -= psize->data.i;
    }

      if (orig_cnt < 1 || psize->data.i < 1 || orig_cnt > MAX_PARTITIONS)
    {
      PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_SIZE);
      goto check_end;
    }
    }

check_end:
  if (tmp_parser != NULL)
    {
      parser_free_parser (tmp_parser);
      tmp_parser = NULL;
    }

  if (column_dt != NULL)
    {
      parser_free_tree (parser, column_dt);
      column_dt = NULL;
    }

  if (maxval)
    {
      pr_free_ext_value (maxval);
    }
  if (minval)
    {
      pr_free_ext_value (minval);
    }
  if (partmax)
    {
      pr_free_ext_value (partmax);
    }
  if (partmin)
    {
      pr_free_ext_value (partmin);
    }

  for (p = minmax_head; p; p = next)
    {
      next = p->next;
      pr_free_ext_value (p->val);
      free_and_init (p);
    }
  for (p = inlist_head; p; p = next)
    {
      next = p->next;
      pr_free_ext_value (p->val);
      free_and_init (p);
    }
  for (p = outlist_head; p; p = next)
    {
      next = p->next;
      pr_free_ext_value (p->val);
      free_and_init (p);
    }

  return;

out_of_mem_fail:
  PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
  goto out_of_mem;

invalid_partition_info_fail:
  PT_ERRORm (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_INFO);

out_of_mem:
  pr_clear_value (&minele);
  pr_clear_value (&maxele);
  goto check_end;
}

/*
 * pt_attr_refers_to_self () - is this a self referencing attribute?
 *   return:  1 if attr refers to self
 *   parser(in): the parser context
 *   attr(in): an attribute
 *   self(in): name of vclass being created/altered
 */
static bool
pt_attr_refers_to_self (PARSER_CONTEXT * parser, PT_NODE * attr, const char *self)
{
  PT_NODE *type;
  DB_OBJECT *self_obj, *attr_obj;

  if (!attr || attr->type_enum != PT_TYPE_OBJECT || !attr->data_type || attr->data_type->node_type != PT_DATA_TYPE
      || !self)
    {
      return false;
    }

  for (type = attr->data_type->info.data_type.entity; type && type->node_type == PT_NAME; type = type->next)
    {
      /* self is a string because in the create case, self does not exist yet */
      if (!pt_user_specified_name_compare (self, type->info.name.original))
    {
      return true;
    }

      /* an attribute whose type is a subclass of self is also considered a self-referencing attribute */
      self_obj = db_find_class (self);
      attr_obj = type->info.name.db_object;
      if (self_obj && attr_obj && db_is_subclass (attr_obj, self_obj) > 0)
    {
      return true;
    }
    }

  return false;
}

/*
 * pt_is_compatible_type () -
 *   return:  true on compatible type
 *   arg1_type(in):
 *   arg2_type(in):
 */
static bool
pt_is_compatible_type (const PT_TYPE_ENUM arg1_type, const PT_TYPE_ENUM arg2_type)
{
  bool is_compatible = false;
  if (arg1_type == arg2_type)
    {
      is_compatible = true;
    }
  else
    switch (arg1_type)
      {
      case PT_TYPE_SMALLINT:
      case PT_TYPE_INTEGER:
      case PT_TYPE_BIGINT:
      case PT_TYPE_FLOAT:
      case PT_TYPE_DOUBLE:
      case PT_TYPE_NUMERIC:
      case PT_TYPE_MONETARY:
    switch (arg2_type)
      {
      case PT_TYPE_SMALLINT:
      case PT_TYPE_INTEGER:
      case PT_TYPE_BIGINT:
      case PT_TYPE_FLOAT:
      case PT_TYPE_DOUBLE:
      case PT_TYPE_NUMERIC:
      case PT_TYPE_MONETARY:
      case PT_TYPE_LOGICAL: /* logical is compatible with these types */
        is_compatible = true;
        break;
      default:
        break;
      }
    break;
      case PT_TYPE_CHAR:
      case PT_TYPE_VARCHAR:
    switch (arg2_type)
      {
      case PT_TYPE_CHAR:
      case PT_TYPE_VARCHAR:
        is_compatible = true;
        break;
      default:
        break;
      }
    break;
      case PT_TYPE_BIT:
      case PT_TYPE_VARBIT:
    switch (arg2_type)
      {
      case PT_TYPE_BIT:
      case PT_TYPE_VARBIT:
        is_compatible = true;
        break;
      default:
        break;
      }
    break;
      default:
    break;
      }

  return is_compatible;
}

/*
 * pt_check_vclass_union_spec () -
 *   return:
 *   parser(in):
 *   qry(in):
 *   attrs(in):
 */
static PT_NODE *
pt_check_vclass_union_spec (PARSER_CONTEXT * parser, PT_NODE * qry, PT_NODE * attrds)
{
  PT_NODE *attrd = NULL;
  PT_NODE *attrs1 = NULL;
  PT_NODE *attrs2 = NULL;
  PT_NODE *att1 = NULL;
  PT_NODE *att2 = NULL;
  PT_NODE *result_stmt = NULL;

  /* parser assures us that it's a query but better make sure */
  if (!pt_is_query (qry))
    {
      return NULL;
    }

  if (!(qry->node_type == PT_UNION || qry->node_type == PT_DIFFERENCE || qry->node_type == PT_INTERSECTION))
    {
      assert (qry->node_type == PT_SELECT);
      return qry;
    }

  result_stmt = pt_check_vclass_union_spec (parser, qry->info.query.q.union_.arg1, attrds);
  if (pt_has_error (parser) || result_stmt == NULL)
    {
      return NULL;
    }
  result_stmt = pt_check_vclass_union_spec (parser, qry->info.query.q.union_.arg2, attrds);
  if (pt_has_error (parser) || result_stmt == NULL)
    {
      return NULL;
    }

  attrs1 = pt_get_select_list (parser, qry->info.query.q.union_.arg1);
  if (attrs1 == NULL)
    {
      return NULL;
    }
  attrs2 = pt_get_select_list (parser, qry->info.query.q.union_.arg2);
  if (attrs2 == NULL)
    {
      return NULL;
    }

  for (attrd = attrds, att1 = attrs1, att2 = attrs2;
       attrd != NULL && att1 != NULL && att2 != NULL; attrd = attrd->next, att1 = att1->next, att2 = att2->next)
    {
      /* bypass any class_attribute in the vclass attribute defs */
      if (attrd->info.attr_def.attr_type == PT_META_ATTR)
    {
      continue;
    }

      /* we have a vclass attribute def context, so do union vclass compatibility checks where applicable */
      if (attrd->type_enum != PT_TYPE_OBJECT)
    {
      continue;
    }

      if (pt_vclass_compatible (parser, attrd->data_type, att1)
      && pt_vclass_compatible (parser, attrd->data_type, att2))
    {
      continue;
    }

      if (!pt_class_assignable (parser, attrd->data_type, att1))
    {
      PT_ERRORmf2 (parser, att1, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ATT_INCOMPATIBLE_COL,
               attribute_name (parser, attrd), pt_short_print (parser, att1));
      return NULL;
    }
      if (!pt_class_assignable (parser, attrd->data_type, att2))
    {
      PT_ERRORmf2 (parser, att2, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ATT_INCOMPATIBLE_COL,
               attribute_name (parser, attrd), pt_short_print (parser, att2));
      return NULL;
    }
    }

  assert (attrd == NULL && att1 == NULL && att2 == NULL);

  return qry;
}

/*
 * pt_check_cyclic_reference_in_view_spec () -
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in):
 */
PT_NODE *
pt_check_cyclic_reference_in_view_spec (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  const char *spec_name = NULL;
  PT_NODE *entity_name = NULL;
  DB_OBJECT *class_object;
  DB_QUERY_SPEC *db_query_spec;
  PT_NODE **result;
  PT_NODE *query_spec;
  const char *query_spec_string;
  const char *self = (const char *) arg;
  PARSER_CONTEXT *query_cache;

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

  switch (node->node_type)
    {
    case PT_SPEC:

      entity_name = node->info.spec.entity_name;
      if (entity_name == NULL)
    {
      return node;
    }
      if (entity_name->node_type != PT_NAME)
    {
      return node;
    }

      spec_name = pt_get_name (entity_name);
      if (pt_str_compare (spec_name, self, CASE_INSENSITIVE) == 0)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CYCLIC_REFERENCE_VIEW_SPEC, self);
      *continue_walk = PT_STOP_WALK;
      return node;
    }

      class_object = entity_name->info.name.db_object;
      if (db_is_vclass (class_object) <= 0)
    {
      return node;
    }

      query_cache = parser_create_parser ();
      if (query_cache == NULL)
    {
      return node;
    }

      db_query_spec = db_get_query_specs (class_object);
      while (db_query_spec)
    {
      query_spec_string = db_query_spec_string (db_query_spec);
      result = parser_parse_string_use_sys_charset (query_cache, query_spec_string);

      if (result != NULL)
        {
          query_spec = *result;
          parser_walk_tree (query_cache, query_spec, pt_check_cyclic_reference_in_view_spec, arg, NULL, NULL);
        }

      if (pt_has_error (query_cache))
        {
          PT_ERROR (parser, node, query_cache->error_msgs->info.error_msg.error_message);

          *continue_walk = PT_STOP_WALK;
          break;
        }

      db_query_spec = db_query_spec_next (db_query_spec);
    }
      parser_free_parser (query_cache);
      break;

    default:
      break;
    }

  return node;
}

/*
 * pt_check_vclass_query_spec () -  do semantic checks on a vclass query spec
 *   return:
 *   parser(in): the parser context used to derive the qry
 *   qry(in): a vclass query specification
 *   attrs(in): the attributes of the vclass
 *   self(in): name of vclass being created/altered
 *
 * Note :
 * check that query_spec:
 * - count(attrs) == count(columns)
 * - corresponding attribute and query_spec column match type-wise
 * - query_spec column that corresponds to a shared attribute must be NA
 */

static PT_NODE *
pt_check_vclass_query_spec (PARSER_CONTEXT * parser, PT_NODE * qry, PT_NODE * attrs, const char *self,
                const bool do_semantic_check)
{
  PT_NODE *columns, *col, *attr;
  int col_count, attr_count;

  if (!pt_is_query (qry))
    {
      return NULL;
    }

  if (qry->info.query.into_list != NULL)
    {
      PT_ERRORm (parser, qry, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_WANT_NO_INTO_CLAUSE);
      return NULL;
    }

  if (do_semantic_check)
    {
      qry = pt_semantic_check (parser, qry);
      if (pt_has_error (parser) || qry == NULL)
    {
      return NULL;
    }
    }

  (void) parser_walk_tree (parser, qry, pt_check_cyclic_reference_in_view_spec, (void *) self, NULL, NULL);
  if (pt_has_error (parser))
    {
      return NULL;
    }

  /* count(attrs) == count(query spec columns) */
  columns = pt_get_select_list (parser, qry);
  col_count = pt_length_of_select_list (columns, EXCLUDE_HIDDEN_COLUMNS);
  attr_count = pt_length_of_list (attrs);
  if (attr_count != col_count)
    {
      PT_ERRORmf2 (parser, qry, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ATT_CNT_NE_COL_CNT, attr_count, col_count);
      return NULL;
    }

  qry = pt_check_vclass_union_spec (parser, qry, attrs);
  if (pt_has_error (parser) || qry == NULL)
    {
      return NULL;
    }

  /* foreach normal/shared attribute and query_spec column do */
  for (attr = attrs, col = columns; attr != NULL && col != NULL; attr = attr->next, col = col->next)
    {
      /* bypass any class_attribute */
      if (attr->info.attr_def.attr_type == PT_META_ATTR)
    {
      attr = attr->next;
      continue;
    }

      if (col->node_type == PT_HOST_VAR)
    {
      PT_ERRORm (parser, col, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_HOSTVAR_NOT_ALLOWED_ON_QUERY_SPEC);
    }
      else if (attr->type_enum == PT_TYPE_NONE)
    {
      if (col->node_type == PT_VALUE && col->type_enum == PT_TYPE_NULL)
        {
          attr->type_enum = PT_TYPE_VARCHAR;
        }
      else
        {
          pt_fixup_column_type (col);

          attr->type_enum = col->type_enum;
          if (col->data_type)
        {
          attr->data_type = parser_copy_tree_list (parser, col->data_type);
        }
        }
    }
      /* attribute and query_spec column must match type-wise */
      else if (attr->type_enum == PT_TYPE_OBJECT)
    {
      if (!pt_attr_refers_to_self (parser, attr, self) && !pt_vclass_compatible (parser, attr->data_type, col)
          && !pt_class_assignable (parser, attr->data_type, col))
        {
          PT_ERRORmf2 (parser, col, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ATT_INCOMPATIBLE_COL,
               attribute_name (parser, attr), pt_short_print (parser, col));
        }
    }
      else if (PT_IS_COLLECTION_TYPE (attr->type_enum))
    {
      if (!pt_collection_assignable (parser, attr, col))
        {
          PT_ERRORmf2 (parser, col, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ATT_INCOMPATIBLE_COL,
               attribute_name (parser, attr), pt_short_print (parser, col));
        }
    }

      /* any shared attribute must correspond to NA in the query_spec */
      if (is_shared_attribute (parser, attr) && col->type_enum != PT_TYPE_NA && col->type_enum != PT_TYPE_NULL)
    {
      PT_ERRORmf (parser, col, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_QSPEC_COL_NOT_NA,
              attribute_name (parser, attr));
    }
    }

  return qry;
}

/*
 * pt_type_cast_vclass_query_spec_column () -
 *   return:  current or new column
 *   parser(in): the parser context used to derive the qry
 *   attr(in): the attributes of the vclass
 *   col(in): the query_spec column of the vclass
 */

PT_NODE *
pt_type_cast_vclass_query_spec_column (PARSER_CONTEXT * parser, PT_NODE * attr, PT_NODE * col)
{
  bool is_object_type;
  PT_UNION_COMPATIBLE c;
  PT_NODE *new_col, *new_dt, *next_col;

  /* guarantees PT_TYPE_OBJECT and SET types are fully compatible. */
  if (attr->type_enum == PT_TYPE_OBJECT || PT_IS_COLLECTION_TYPE (attr->type_enum))
    {
      return col;
    }

  c = pt_union_compatible (parser, attr, col, true, &is_object_type);
  if (((c == PT_UNION_COMP)
       && (attr->type_enum == col->type_enum && PT_IS_PARAMETERIZED_TYPE (attr->type_enum) && attr->data_type
       && col->data_type)
       && ((attr->data_type->info.data_type.precision != col->data_type->info.data_type.precision)
       || (attr->data_type->info.data_type.dec_precision != col->data_type->info.data_type.dec_precision)))
      || (c == PT_UNION_INCOMP))
    {
      /* rewrite */
      next_col = col->next;
      col->next = NULL;
      new_col = new_dt = NULL;

      new_col = parser_new_node (parser, PT_EXPR);
      if (new_col == NULL)
    {
      PT_ERRORm (parser, col, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      return col;       /* give up */
    }

      new_dt = parser_new_node (parser, PT_DATA_TYPE);
      if (new_dt == NULL)
    {
      PT_ERRORm (parser, col, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      parser_free_tree (parser, new_col);
      return col;       /* give up */
    }

      /* move alias */
      new_col->line_number = col->line_number;
      new_col->column_number = col->column_number;
      new_col->alias_print = col->alias_print;
      col->alias_print = NULL;
      new_dt->type_enum = attr->type_enum;
      if (attr->data_type)
    {
      new_dt->info.data_type.precision = attr->data_type->info.data_type.precision;
      new_dt->info.data_type.dec_precision = attr->data_type->info.data_type.dec_precision;
      new_dt->info.data_type.units = attr->data_type->info.data_type.units;
      new_dt->info.data_type.collation_id = attr->data_type->info.data_type.collation_id;
      assert (new_dt->info.data_type.collation_id >= 0);
      new_dt->info.data_type.enumeration =
        parser_copy_tree_list (parser, attr->data_type->info.data_type.enumeration);
    }
      new_col->type_enum = new_dt->type_enum;
      new_col->info.expr.op = PT_CAST;
      new_col->info.expr.cast_type = new_dt;
      new_col->info.expr.arg1 = col;
      new_col->next = next_col;
      new_col->data_type = parser_copy_tree_list (parser, new_dt);
      PT_EXPR_INFO_SET_FLAG (new_col, PT_EXPR_INFO_CAST_SHOULD_FOLD);
      col = new_col;
    }

  return col;
}

/*
 * pt_type_cast_vclass_query_spec () -
 *   return:
 *   parser(in):
 *   qry(in):
 *   attrs(in):
 */
static PT_NODE *
pt_type_cast_vclass_query_spec (PARSER_CONTEXT * parser, PT_NODE * qry, PT_NODE * attrs)
{
  PT_NODE *columns, *col, *attr;
  PT_NODE *new_col, *prev_col;
  PT_NODE *node_list;

  /* parser assures us that it's a query but better make sure */
  if (!pt_is_query (qry))
    {
      return NULL;
    }

  if (qry->node_type != PT_SELECT)
    {
      if (!pt_type_cast_vclass_query_spec (parser, qry->info.query.q.union_.arg1, attrs) || pt_has_error (parser)
      || (!pt_type_cast_vclass_query_spec (parser, qry->info.query.q.union_.arg2, attrs)) || pt_has_error (parser))
    {
      return NULL;
    }
    }

  if (qry->node_type != PT_SELECT)
    {
      return qry;       /* already done */
    }

  if (PT_IS_VALUE_QUERY (qry))
    {
      for (node_list = qry->info.query.q.select.list; node_list != NULL; node_list = node_list->next)
    {
      assert (node_list->node_type == PT_NODE_LIST);

      columns = node_list->info.node_list.list;

      col = columns;
      attr = attrs;
      prev_col = NULL;

      while (attr != NULL && col != NULL)
        {
          /* skip class attribute */
          if (attr->info.attr_def.attr_type == PT_META_ATTR)
        {
          attr = attr->next;
          continue;
        }

          new_col = pt_type_cast_vclass_query_spec_column (parser, attr, col);
          if (new_col != col)
        {
          if (prev_col == NULL)
            {
              node_list->info.node_list.list = new_col;
              qry->type_enum = new_col->type_enum;
              if (qry->data_type)
            {
              parser_free_tree (parser, qry->data_type);
            }
              qry->data_type = parser_copy_tree_list (parser, new_col->data_type);
            }
          else
            {
              prev_col->next = new_col;
            }

          col = new_col;
        }

          prev_col = col;
          col = col->next;
          attr = attr->next;
        }
    }
    }
  else
    {

      columns = pt_get_select_list (parser, qry);

      /* foreach normal/shared attribute and query_spec column do */
      attr = attrs;
      col = columns;
      prev_col = NULL;

      while (attr && col)
    {
      /* bypass any class_attribute */
      if (attr->info.attr_def.attr_type == PT_META_ATTR)
        {
          attr = attr->next;
          continue;
        }

      new_col = pt_type_cast_vclass_query_spec_column (parser, attr, col);
      if (new_col != col)
        {
          if (prev_col == NULL)
        {
          qry->info.query.q.select.list = new_col;
          qry->type_enum = new_col->type_enum;
          if (qry->data_type)
            {
              parser_free_tree (parser, qry->data_type);
            }
          qry->data_type = parser_copy_tree_list (parser, new_col->data_type);
        }
          else
        {
          prev_col->next = new_col;
        }

          col = new_col;
        }

      /* save previous link */
      prev_col = col;
      /* advance to next attribute and column */
      attr = attr->next;
      col = col->next;
    }
    }

  return qry;
}

/*
 * pt_check_default_vclass_query_spec () -
 *   return: new attrs node including default values
 *   parser(in):
 *   qry(in):
 *   attrs(in):
 *
 * For all those view attributes that don't have implicit default values,
 * copy the default values from the original table
 *
 * NOTE: there are two ways attrs is constructed at this point:
 *  - stmt: create view (attr_list) as select... and the attrs will be created directly from the statement
 *  - stmt: create view as select... and the attrs will be created from the query's select list
 * In both cases, each attribute in attrs will correspond to the column in the select list at the same index
 */
static PT_NODE *
pt_check_default_vclass_query_spec (PARSER_CONTEXT * parser, PT_NODE * qry, PT_NODE * attrs)
{
  PT_NODE *attr, *col;
  PT_NODE *columns = pt_get_select_list (parser, qry);
  PT_NODE *default_data = NULL;
  PT_NODE *default_value = NULL, *default_op_value = NULL;
  PT_NODE *spec, *entity_name;
  DB_OBJECT *obj;
  DB_ATTRIBUTE *col_attr;
  const char *lang_str;
  int flag = 0;
  bool has_user_format;

  /* Import default value and on update default expr from referenced table
   * for those attributes in the the view that don't have them. */
  for (attr = attrs, col = columns; attr && col; attr = attr->next, col = col->next)
    {
      if (attr->info.attr_def.data_default != NULL && attr->info.attr_def.on_update != DB_DEFAULT_NONE)
    {
      /* default values are overwritten */
      continue;
    }
      if (col->node_type != PT_NAME)
    {
      continue;
    }
      if (col->info.name.spec_id == 0)
    {
      continue;
    }

      spec = (PT_NODE *) col->info.name.spec_id;
      entity_name = spec->info.spec.entity_name;
      if (entity_name == NULL || !PT_IS_NAME_NODE (entity_name))
    {
      continue;
    }

      obj = entity_name->info.name.db_object;
      if (obj == NULL)
    {
      continue;
    }

      col_attr = db_get_attribute_force (obj, col->info.name.original);
      if (col_attr == NULL)
    {
      continue;
    }

      if (attr->info.attr_def.on_update == DB_DEFAULT_NONE)
    {
      attr->info.attr_def.on_update = col_attr->on_update_default_expr;
    }

      if (attr->info.attr_def.data_default == NULL)
    {
      if (DB_IS_NULL (&col_attr->default_value.value)
          && (col_attr->default_value.default_expr.default_expr_type == DB_DEFAULT_NONE))
        {
          /* don't create any default node if default value is null unless default expression type is not
           * DB_DEFAULT_NONE */
          continue;
        }

      if (col_attr->default_value.default_expr.default_expr_type == DB_DEFAULT_NONE)
        {
          default_value = pt_dbval_to_value (parser, &col_attr->default_value.value);
          if (default_value == NULL)
        {
          PT_ERRORm (parser, qry, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          goto error;
        }

          default_data = parser_new_node (parser, PT_DATA_DEFAULT);
          if (default_data == NULL)
        {
          parser_free_tree (parser, default_value);
          PT_ERRORm (parser, qry, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          goto error;
        }
          default_data->info.data_default.default_value = default_value;
          default_data->info.data_default.shared = PT_DEFAULT;
          default_data->info.data_default.default_expr_type = DB_DEFAULT_NONE;
        }
      else
        {
          default_op_value = parser_new_node (parser, PT_EXPR);
          if (default_op_value == NULL)
        {
          PT_ERRORm (parser, qry, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          goto error;
        }

          default_op_value->info.expr.op =
        pt_op_type_from_default_expr_type (col_attr->default_value.default_expr.default_expr_type);

          if (col_attr->default_value.default_expr.default_expr_op != T_TO_CHAR)
        {
          default_value = default_op_value;
        }
          else
        {
          PT_NODE *arg1, *arg2, *arg3;

          arg1 = default_op_value;
          has_user_format = col_attr->default_value.default_expr.default_expr_format ? 1 : 0;
          arg2 = pt_make_string_value (parser, col_attr->default_value.default_expr.default_expr_format);
          if (arg2 == NULL)
            {
              parser_free_tree (parser, default_op_value);
              PT_ERRORm (parser, qry, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
              goto error;
            }

          arg3 = parser_new_node (parser, PT_VALUE);
          if (arg3 == NULL)
            {
              parser_free_tree (parser, default_op_value);
              parser_free_tree (parser, arg2);
            }
          arg3->type_enum = PT_TYPE_INTEGER;
          lang_str = prm_get_string_value (PRM_ID_INTL_DATE_LANG);
          lang_set_flag_from_lang (lang_str, has_user_format, 0, &flag);
          arg3->info.value.data_value.i = (long) flag;

          default_value = parser_make_expression (parser, PT_TO_CHAR, arg1, arg2, arg3);
          if (default_value == NULL)
            {
              parser_free_tree (parser, default_op_value);
              parser_free_tree (parser, arg2);
              parser_free_tree (parser, arg3);
              PT_ERRORm (parser, qry, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
              goto error;
            }
        }

          default_data = parser_new_node (parser, PT_DATA_DEFAULT);
          if (default_data == NULL)
        {
          parser_free_tree (parser, default_value);
          PT_ERRORm (parser, qry, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          goto error;
        }

          default_data->info.data_default.default_value = default_value;
          default_data->info.data_default.shared = PT_DEFAULT;
          default_data->info.data_default.default_expr_type =
        col_attr->default_value.default_expr.default_expr_type;
        }

      attr->info.attr_def.data_default = default_data;
    }
    }

  return attrs;

error:
  parser_free_tree (parser, attrs);
  return NULL;
}

/*
 * pt_check_create_view () - do semantic checks on a create vclass statement
 *   or an "ALTER VIEW AS SELECT" statement
 *
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   stmt(in): a create vclass statement
 *
 * Note :
 * This function is also called when doing "ALTER VIEW xxx AS SELECT ...",
 * which is a simplified case, since it does not support class inheritance.
 *
 * check that
 * - stmt's query_specs are union compatible with each other
 * - if no attributes are given, derive them from the query_spec columns
 * - if an attribute has no data type then derive it from its
 *   matching query_spec column
 * - corresponding attribute and query_spec column must match type-wise
 * - count(attributes) == count(query_spec columns)
 * - query_spec column that corresponds to a shared attribute must be NA
 * - we allow order by clauses in the queries
 */

static void
pt_check_create_view (PARSER_CONTEXT * parser, PT_NODE * stmt)
{
  PT_NODE *all_attrs = NULL;
  PT_NODE *derived_attr = NULL;
  PT_NODE *result_stmt = NULL;
  PT_NODE **qry_specs_ptr = NULL;
  PT_NODE *crt_qry = NULL;
  PT_NODE **prev_qry_link_ptr = NULL;
  PT_NODE **attr_def_list_ptr = NULL;
  PT_NODE *prev_qry;
  const char *name = NULL;
  int attr_count = 0;
  bool do_not_replace_orderby;

  assert (parser != NULL);

  if (stmt == NULL)
    {
      return;
    }
  if (stmt->node_type == PT_CREATE_ENTITY)
    {
      if (stmt->info.create_entity.entity_type != PT_VCLASS || stmt->info.create_entity.entity_name == NULL)
    {
      return;
    }
    }
  else if ((stmt->node_type == PT_ALTER) && (stmt->info.alter.code == PT_RESET_QUERY))
    {
      if (stmt->info.alter.entity_type != PT_VCLASS || stmt->info.alter.entity_name == NULL)
    {
      return;
    }
    }
  else
    {
      return;
    }


  if (stmt->node_type == PT_CREATE_ENTITY)
    {
      name = stmt->info.create_entity.entity_name->info.name.original;
      qry_specs_ptr = &stmt->info.create_entity.as_query_list;
      attr_def_list_ptr = &stmt->info.create_entity.attr_def_list;
    }
  else
    {
      assert ((stmt->node_type == PT_ALTER) && (stmt->info.alter.code == PT_RESET_QUERY));
      name = stmt->info.alter.entity_name->info.name.original;
      qry_specs_ptr = &stmt->info.alter.alter_clause.query.query;
      attr_def_list_ptr = &stmt->info.alter.alter_clause.query.attr_def_list;
    }

  if (*qry_specs_ptr == NULL || pt_has_error (parser))
    {
      return;
    }

  prev_qry = NULL;
  for (crt_qry = *qry_specs_ptr, prev_qry_link_ptr = qry_specs_ptr; crt_qry != NULL;
       prev_qry_link_ptr = &crt_qry->next, crt_qry = crt_qry->next)
    {
      PT_NODE *const save_next = crt_qry->next;

      crt_qry->next = NULL;

      /* TODO This seems to flag too many queries as view specs because it also traverses the tree to subqueries. It
       * might need a pre_function that returns PT_STOP_WALK for subqueries. */
      do_not_replace_orderby = true;
      result_stmt =
    parser_walk_tree (parser, crt_qry, pt_set_is_view_spec, (void *) &do_not_replace_orderby, NULL, NULL);
      if (result_stmt == NULL)
    {
      assert (false);
      PT_ERRORm (parser, stmt, MSGCAT_SET_ERROR, -(ER_GENERIC_ERROR));
      return;
    }
      crt_qry = result_stmt;

      result_stmt = pt_semantic_check (parser, crt_qry);
      if (pt_has_error (parser))
    {
      if (prev_qry)
        {
          prev_qry->next = save_next;
        }
      crt_qry = NULL;
      (*prev_qry_link_ptr) = crt_qry;
      return;
    }

      if (result_stmt == NULL)
    {
      assert (false);
      PT_ERRORm (parser, stmt, MSGCAT_SET_ERROR, -(ER_GENERIC_ERROR));
      return;
    }

      if (pt_has_parameters (parser, result_stmt))
    {
      PT_ERRORmf (parser, crt_qry, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_VARIABLE_NOT_ALLOWED, 0);
      return;
    }

      crt_qry = result_stmt;

      crt_qry->next = save_next;
      (*prev_qry_link_ptr) = crt_qry;
      prev_qry = crt_qry;
    }

  attr_count = pt_number_of_attributes (parser, stmt, &all_attrs);

  /* if no attributes are given, try to derive them from the query_spec columns. */
  if (attr_count <= 0)
    {
      PT_NODE *crt_attr = NULL;
      PT_NODE *const qspec_attr = pt_get_select_list (parser, *qry_specs_ptr);

      assert (attr_count == 0);
      assert (*attr_def_list_ptr == NULL);

      for (crt_attr = qspec_attr; crt_attr != NULL; crt_attr = crt_attr->next)
    {
      PT_NODE *s_attr = NULL;

      if (crt_attr->alias_print)
        {
          s_attr = crt_attr;
        }
      else
        {
          /* allow attributes to be derived only from path expressions. */
          s_attr = pt_get_end_path_node (crt_attr);
          if (s_attr->node_type != PT_NAME)
        {
          s_attr = NULL;
        }
        }

      if (s_attr == NULL)
        {
          PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_MISSING_ATTR_NAME,
              pt_short_print (parser, crt_attr));
          return;
        }
      else if (s_attr->node_type == PT_HOST_VAR)
        {
          PT_ERRORm (parser, s_attr, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_HOSTVAR_NOT_ALLOWED_ON_QUERY_SPEC);
          return;
        }
      else if (s_attr->node_type == PT_VALUE && s_attr->type_enum == PT_TYPE_NULL)
        {
          s_attr->type_enum = PT_TYPE_VARCHAR;
        }

      derived_attr = pt_derive_attribute (parser, s_attr);
      if (derived_attr == NULL)
        {
          PT_ERRORm (parser, stmt, MSGCAT_SET_ERROR, -(ER_GENERIC_ERROR));
          return;
        }

      *attr_def_list_ptr = parser_append_node (derived_attr, *attr_def_list_ptr);
    }

      attr_count = pt_number_of_attributes (parser, stmt, &all_attrs);
    }

  assert (attr_count >= 0);

  /* do other checks on query specs */
  for (crt_qry = *qry_specs_ptr, prev_qry_link_ptr = qry_specs_ptr; crt_qry != NULL;
       prev_qry_link_ptr = &crt_qry->next, crt_qry = crt_qry->next)
    {
      PT_NODE *const save_next = crt_qry->next;

      crt_qry->next = NULL;

      result_stmt = pt_check_vclass_query_spec (parser, crt_qry, all_attrs, name, false);
      if (pt_has_error (parser))
    {
      return;
    }
      if (result_stmt == NULL)
    {
      PT_ERRORm (parser, stmt, MSGCAT_SET_ERROR, -(ER_GENERIC_ERROR));
      return;
    }
      crt_qry = result_stmt;

      all_attrs = pt_check_default_vclass_query_spec (parser, crt_qry, all_attrs);
      if (pt_has_error (parser))
    {
      return;
    }
      if (all_attrs == NULL)
    {
      PT_ERRORm (parser, stmt, MSGCAT_SET_ERROR, -(ER_GENERIC_ERROR));
      return;
    }

      result_stmt = pt_type_cast_vclass_query_spec (parser, crt_qry, all_attrs);
      if (pt_has_error (parser))
    {
      return;
    }
      if (result_stmt == NULL)
    {
      assert (false);
      PT_ERRORm (parser, stmt, MSGCAT_SET_ERROR, -(ER_GENERIC_ERROR));
      return;
    }
      crt_qry = result_stmt;

      crt_qry->next = save_next;
      (*prev_qry_link_ptr) = crt_qry;
    }
}

/*
 * pt_check_create_user () - semantic check a create user statement
 * return   : none
 * parser(in)   : the parser context
 * node(in) : create user node
 */
static void
pt_check_create_user (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *user_name;
  const char *name;
  int name_upper_size;

  if (!node)
    {
      return;
    }
  if (node->node_type != PT_CREATE_USER)
    {
      return;
    }

  user_name = node->info.create_user.user_name;
  if (user_name->node_type != PT_NAME)
    {
      return;
    }

  name = user_name->info.name.original;
  if (name == NULL)
    {
      return;
    }
  name_upper_size = intl_identifier_upper_string_size (name);
  if (name_upper_size >= DB_MAX_USER_LENGTH)
    {
      PT_ERRORm (parser, user_name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_USER_NAME_TOO_LONG);
    }
}

static PT_NODE *
pt_check_query_cache_in_create_entity (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  bool *has_cache_hint = (bool *) arg;

  *continue_walk = PT_CONTINUE_WALK;

  if (node->node_type == PT_SELECT)
    {
      if (node->info.query.hint & PT_HINT_QUERY_CACHE)
    {
      *has_cache_hint = true;
      *continue_walk = PT_STOP_WALK;
    }
    }

  return node;
}

/*
 * pt_check_create_entity () - semantic check a create class/vclass
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   node(in): a create statement
 */

static void
pt_check_create_entity (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *parent, *qry_specs, *name, *create_like;
  PT_NODE *all_attrs, *r, *resolv_class, *attr;
  PT_NODE *tbl_opt_charset, *cs_node, *tbl_opt_coll, *coll_node;
  PT_NODE *tbl_opt = NULL;
  PT_MISC_TYPE entity_type;
  DB_OBJECT *db_obj, *existing_entity;
  DB_OBJECT *owner = NULL;
  const char *owner_name = NULL;
  int found, partition_status = DB_NOT_PARTITIONED_CLASS;
  int collation_id, charset;
  bool found_reuse_oid_option = false, reuse_oid = false;
  bool found_auto_increment = false;
  bool found_tbl_comment = false;
  bool found_tbl_encrypt = false;
  int error = NO_ERROR;

  entity_type = node->info.create_entity.entity_type;

  if (entity_type != PT_CLASS && entity_type != PT_VCLASS)
    {
      /* control should never reach here if tree is well-formed */
      assert (false);
      return;
    }

  tbl_opt_charset = tbl_opt_coll = NULL;
  for (tbl_opt = node->info.create_entity.table_option_list; tbl_opt != NULL; tbl_opt = tbl_opt->next)
    {
      assert (tbl_opt->node_type == PT_TABLE_OPTION);

      switch (tbl_opt->info.table_option.option)
    {
    case PT_TABLE_OPTION_REUSE_OID:
      {
        if (found_reuse_oid_option)
          {
        PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DUPLICATE_TABLE_OPTION,
                parser_print_tree (parser, tbl_opt));
        return;
          }
        else
          {
        found_reuse_oid_option = true;
        reuse_oid = true;
          }
      }
      break;

    case PT_TABLE_OPTION_DONT_REUSE_OID:
      {
        if (found_reuse_oid_option)
          {
        PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DUPLICATE_TABLE_OPTION,
                parser_print_tree (parser, tbl_opt));
        return;
          }
        else
          {
        found_reuse_oid_option = true;
        reuse_oid = false;
          }
      }
      break;

    case PT_TABLE_OPTION_AUTO_INCREMENT:
      {
        if (found_auto_increment)
          {
        PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DUPLICATE_TABLE_OPTION,
                parser_print_tree (parser, tbl_opt));
        return;
          }
        else
          {
        if (tbl_opt->info.table_option.val != NULL)
          {
            found_auto_increment = true;
            assert (tbl_opt->info.table_option.val->node_type == PT_VALUE);
            assert (tbl_opt->info.table_option.val->type_enum == PT_TYPE_NUMERIC);
          }
          }
      }
      break;

    case PT_TABLE_OPTION_CHARSET:
      {
        if (tbl_opt_charset != NULL)
          {
        PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DUPLICATE_TABLE_OPTION,
                parser_print_tree (parser, tbl_opt));
        return;
          }
        else
          {
        tbl_opt_charset = tbl_opt;
          }
      }
      break;

    case PT_TABLE_OPTION_COLLATION:
      {
        if (tbl_opt_coll != NULL)
          {
        PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DUPLICATE_TABLE_OPTION,
                parser_print_tree (parser, tbl_opt));
        return;
          }
        else
          {
        tbl_opt_coll = tbl_opt;
          }
      }
      break;
    case PT_TABLE_OPTION_COMMENT:
      {
        if (found_tbl_comment)
          {
        PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DUPLICATE_TABLE_OPTION,
                parser_print_tree (parser, tbl_opt));
        return;
          }
        else
          {
        found_tbl_comment = true;
          }
      }
      break;
    case PT_TABLE_OPTION_ENCRYPT:
      {
        if (found_tbl_encrypt)
          {
        PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DUPLICATE_TABLE_OPTION,
                parser_print_tree (parser, tbl_opt));
        return;
          }
        else
          {
        found_tbl_encrypt = true;
          }
      }
      break;
    default:
      /* should never arrive here */
      assert (false);
      break;
    }
    }

  /* get default value of reuse_oid from system parameter and create pt_node and add it into table_option_list, if don't use table option related reuse_oid */
  if (!found_reuse_oid_option && entity_type == PT_CLASS)
    {
      PT_NODE *tmp;

      reuse_oid = prm_get_bool_value (PRM_ID_TB_DEFAULT_REUSE_OID);
      tmp = pt_table_option (parser, reuse_oid ? PT_TABLE_OPTION_REUSE_OID : PT_TABLE_OPTION_DONT_REUSE_OID, NULL);

      if (tmp)
    {
      tmp->next = node->info.create_entity.table_option_list;
      node->info.create_entity.table_option_list = tmp;
    }
    }

  /* validate charset and collation options, if any */
  cs_node = (tbl_opt_charset) ? tbl_opt_charset->info.table_option.val : NULL;
  coll_node = (tbl_opt_coll) ? tbl_opt_coll->info.table_option.val : NULL;
  charset = LANG_SYS_CODESET;
  collation_id = LANG_SYS_COLLATION;
  if ((cs_node != NULL || coll_node != NULL)
      && pt_check_grammar_charset_collation (parser, cs_node, coll_node, &charset, &collation_id) != NO_ERROR)
    {
      return;
    }

  name = node->info.create_entity.entity_name;
  owner_name = pt_get_qualifier_name (parser, name);
  if (owner_name)
    {
      owner = db_find_user (owner_name);
      if (owner == NULL)
    {
      ASSERT_ERROR_AND_SET (error);

      if (er_errid () == ER_AU_INVALID_USER)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_USER_IS_NOT_IN_DB, owner_name);
        }

      return;
    }

      if (!ws_is_same_object (owner, Au_user) && !au_is_dba_group_member (Au_user))
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CREATE_TABLE_VIEW_NOT_OWNER);
      return;
    }
    }
  else
    {
      /* In system class names, owner name can be NULL. Otherwise, owner name must not be NULL. */
      assert (au_is_dba_group_member (Au_user));
      assert (sm_check_system_class_by_name (PT_NAME_ORIGINAL (name)));
    }

  /* We cannot use an existing synonym name as a class name. */
  if (db_find_synonym (name->info.name.original) != NULL)
    {
      PT_ERRORmf (parser, name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_EXISTS, name->info.name.original);
      return;
    }
  else
    {
      /* db_find_synonym () == NULL */
      ASSERT_ERROR ();

      if (er_errid () == ER_SYNONYM_NOT_EXIST)
    {
      er_clear ();
    }
      else
    {
      return;
    }
    }

  /* check name doesn't already exist as a class */
  existing_entity = pt_find_class (parser, name, false);
  if (existing_entity != NULL)
    {
      if (!(entity_type == PT_VCLASS && node->info.create_entity.or_replace == 1 && db_is_vclass (existing_entity) > 0)
      /* If user sets "IF NOT EXISTS", do not throw ERROR if TABLE or VIEW with the same name exists, to stay
       * compatible with MySQL. */
      && !(entity_type == PT_CLASS && node->info.create_entity.if_not_exists == 1))
    {
      PT_ERRORmf (parser, name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_EXISTS, name->info.name.original);
      return;
    }
    }
  else
    {
      if (er_errid () == ER_LC_UNKNOWN_CLASSNAME && er_get_severity () == ER_WARNING_SEVERITY)
    {
      /* Because the class is still inexistent, normally, here we will have to encounter some errors/warnings like
       * ER_LC_UNKNOWN_CLASSNAME which is unuseful for current context indeed and may disturb other subsequent
       * routines. Thus, we can/should clear the errors safely. */
      er_clear ();
    }
    }

  pt_check_user_exists (parser, name);

  /* check uniqueness of non-inherited attribute names */
  all_attrs = node->info.create_entity.attr_def_list;
  pt_check_unique_attr (parser, name->info.name.original, all_attrs, PT_ATTR_DEF);

  if (entity_type == PT_CLASS)
    {
      /* for regular classes (no views) set default collation if needed */
      for (attr = node->info.create_entity.attr_def_list; attr; attr = attr->next)
    {
      if (attr->node_type == PT_ATTR_DEF && PT_HAS_COLLATION (attr->type_enum))
        {
          if (pt_attr_check_default_cs_coll (parser, attr, charset, collation_id) != NO_ERROR)
        {
          return;
        }
        }
    }
    }

  /* enforce composition hierarchy restrictions on attr type defs */
  pt_check_attribute_domain (parser, all_attrs, entity_type, name->info.name.original, reuse_oid, node);

  /* check that any and all super classes do exist */
  for (parent = node->info.create_entity.supclass_list; parent != NULL; parent = parent->next)
    {
      db_obj = pt_find_class (parser, parent, false);
      if (db_obj != NULL)
    {
      parent->info.name.db_object = db_obj;
      error = sm_partitioned_class_type (db_obj, &partition_status, NULL, NULL);
      if (error != NO_ERROR)
        {
          PT_ERROR (parser, node, er_msg ());
          break;
        }
      if (partition_status == DB_PARTITION_CLASS)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_NOT_ALLOWED_ACCESS_TO_PARTITION,
              parent->info.name.original);
          break;
        }
      pt_check_user_owns_class (parser, parent);
#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
      if (sm_has_text_domain (db_get_attributes (db_obj), 1))
        {
          /* prevent to define it as a superclass */
          er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_REGU_NOT_IMPLEMENTED, 1, rel_major_release_string ());
          PT_ERROR (parser, node, er_msg ());
          break;
        }
#endif /* ENABLE_UNUSED_FUNCTION */
    }
      else
    {
      PT_ERRORmf2 (parser, parent, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NONEXISTENT_SUPCLASS,
               parent->info.name.original, pt_short_print (parser, node));
      return;
    }
    }

  if (error != NO_ERROR)
    {
      return;
    }

  /* an INHERIT_clause in a create_vclass_stmt without a SUBCLASS_clause is not meaningful. */
  if (node->info.create_entity.resolution_list && !node->info.create_entity.supclass_list)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_WANT_SUBCLASS_CLAUSE,
          pt_short_print_l (parser, node->info.create_entity.resolution_list));
      return;
    }
  else
    {
      /* the INHERIT_clause can only reference classes that are in the SUBCLASS_clause. */
      for (r = node->info.create_entity.resolution_list; r; r = r->next)
    {
      resolv_class = r->info.resolution.of_sup_class_name;
      found = 0;

      for (parent = node->info.create_entity.supclass_list; parent && !found; parent = parent->next)
        {
          found = !pt_user_specified_name_compare (resolv_class->info.name.original, parent->info.name.original);
        }

      if (!found)
        {
          PT_ERRORmf2 (parser, resolv_class, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_IN_SUBCLASS_LIST,
               parser_print_tree (parser, r), pt_short_print_l (parser,
                                        node->info.create_entity.supclass_list));
          return;
        }
    }
    }

  if (entity_type == PT_VCLASS)
    {
      /* The grammar restricts table options to CREATE CLASS / TABLE */
      assert (node->info.create_entity.table_option_list == NULL);

      for (attr = all_attrs; attr; attr = attr->next)
    {
      if (attr->info.attr_def.auto_increment != NULL)
        {
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_VCLASS_ATT_CANT_BE_AUTOINC);
          return;
        }
    }
    }

  qry_specs = node->info.create_entity.as_query_list;
  if (node->info.create_entity.entity_type == PT_CLASS)
    {
      PT_NODE *select = node->info.create_entity.create_select;
      PT_NODE *crt_attr = NULL;
      PT_NODE *const qspec_attr = pt_get_select_list (parser, select);

      /* simple CLASSes must not have any query specs */
      if (qry_specs)
    {
      PT_ERRORm (parser, qry_specs, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_NO_QUERY_SPEC);
      return;
    }
      /* user variables are not allowed in select list for CREATE ... AS SELECT ... */
      if (select != NULL)
    {
      for (crt_attr = qspec_attr; crt_attr != NULL; crt_attr = crt_attr->next)
        {
          if (crt_attr->alias_print == NULL && crt_attr->node_type != PT_NAME && crt_attr->node_type != PT_DOT_)
        {
          PT_ERRORmf (parser, qry_specs, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_MISSING_ATTR_NAME,
                  pt_short_print (parser, crt_attr));
          return;
        }
        }

      if (select->info.query.with != NULL)
        {
          // run semantic check only for CREATE ... AS WITH ...
          if ((crt_attr = pt_check_with_clause (parser, select)) != NULL)
        {
          /* no alias for CTE select list */
          PT_ERRORmf (parser, qry_specs, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_MISSING_ATTR_NAME,
                  pt_short_print (parser, crt_attr));
          return;
        }

          select = pt_semantic_check (parser, select);
        }
      /* INSERT ... SELECT needs to do a semantic check to handle the subquery cache. */
      else
        {
          bool has_cache_hint = false;

          (void *) parser_walk_tree (parser, select, pt_check_query_cache_in_create_entity, &has_cache_hint, NULL,
                     NULL);

          if (has_cache_hint)
        {
          select = pt_semantic_check (parser, select);
        }
        }

      if (pt_has_parameters (parser, select))
        {
          PT_ERRORmf (parser, select, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_VARIABLE_NOT_ALLOWED, 0);
          return;
        }
    }
      if (!pt_has_error (parser) && node->info.create_entity.partition_info)
    {
      pt_check_partitions (parser, node, NULL);
    }
    }
  else              /* must be a CREATE VCLASS statement */
    {
      MOP me = NULL;
      MOP owner = NULL;
      bool change_owner_flag = false;

      if (db_client_type_is_loaddb ())
    {
      const char *user_name = pt_get_qualifier_name (parser, node->info.create_entity.entity_name);
      if (user_name != NULL)
        {
          me = db_get_user ();
          owner = au_find_user (user_name);
          if (owner != NULL && !ws_is_same_object (owner, me))
        {
          /* set user to owner to translate query specification. */
          AU_SET_USER (owner);
          change_owner_flag = true;
        }
        }
    }

      pt_check_create_view (parser, node);

      if (change_owner_flag)
    {
      /* restore user */
      AU_SET_USER (me);
    }
    }

  /* check that all constraints look valid */
  if (!pt_has_error (parser))
    {
      (void) pt_check_constraints (parser, node);
    }

  /*
   * check the auto_increment table option, AND REWRITE IT as
   * a constraint for the (single) AUTO_INCREMENT column.
   */
  if (found_auto_increment)
    {
      (void) pt_check_auto_increment_table_option (parser, node);
    }

  create_like = node->info.create_entity.create_like;
  if (create_like != NULL)
    {
      assert (entity_type == PT_CLASS);

      db_obj = pt_find_class (parser, create_like, false);
      if (db_obj == NULL)
    {
      PT_ERRORmf (parser, create_like, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_EXIST,
              create_like->info.name.original);
      return;
    }
      else
    {
      create_like->info.name.db_object = db_obj;
      pt_check_user_owns_class (parser, create_like);
      if (db_is_class (db_obj) <= 0)
        {
          PT_ERRORmf2 (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_A,
               create_like->info.name.original, pt_show_misc_type (PT_CLASS));
          return;
        }
    }
    }

  for (attr = node->info.create_entity.attr_def_list; attr; attr = attr->next)
    {
      attr->info.attr_def.data_default = pt_check_data_default (parser, attr->info.attr_def.data_default);
    }
}

/*
 * pt_check_create_index () - semantic check a create index
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   node(in): a create index statement
 */

static void
pt_check_create_index (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *name, *prefix_length, *col, *col_expr;
  DB_OBJECT *db_obj;
  int is_partition = DB_NOT_PARTITIONED_CLASS;

  /* check that there trying to create an index on a class */
  name = node->info.index.indexed_class->info.spec.entity_name;

  /* We cannot create index of a class by using synonym names. */
  if (db_find_synonym (name->info.name.original) != NULL)
    {
      PT_ERRORmf (parser, name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_A_CLASS, name->info.name.original);
      return;
    }
  else
    {
      /* db_find_synonym () == NULL */
      ASSERT_ERROR ();

      if (er_errid () == ER_SYNONYM_NOT_EXIST)
    {
      er_clear ();
    }
      else
    {
      return;
    }
    }

  db_obj = db_find_class (name->info.name.original);
  if (db_obj == NULL)
    {
      PT_ERRORmf (parser, name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_A_CLASS, name->info.name.original);
      return;
    }
  else
    {
      /* make sure it's not a virtual class */
      if (db_is_class (db_obj) <= 0)
    {
      PT_ERRORm (parser, name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NO_INDEX_ON_VCLASS);
      return;
    }
      /* check if this is a partition class */
      if (sm_partitioned_class_type (db_obj, &is_partition, NULL, NULL) != NO_ERROR)
    {
      PT_ERROR (parser, node, er_msg ());
      return;
    }

      if (is_partition == DB_PARTITION_CLASS)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PARTITION_REQUEST);
      return;
    }

      /* Check if the columns are valid. We only allow expressions and attribute names. The actual expressions will be
       * validated later, we're only interested in the node type */
      for (col = node->info.index.column_names; col != NULL; col = col->next)
    {
      if (col->node_type != PT_SORT_SPEC)
        {
          assert_release (col->node_type == PT_SORT_SPEC);
          return;
        }
      col_expr = col->info.sort_spec.expr;
      if (col_expr == NULL)
        {
          continue;
        }
      if (col_expr->node_type == PT_NAME)
        {
          /* make sure this is not a parameter */
          if (col_expr->info.name.meta_class != PT_NORMAL)
        {
          PT_ERRORmf (parser, col_expr, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_INDEX_COLUMN,
                  pt_short_print (parser, col_expr));
          return;
        }
        }
      else if (col_expr->node_type != PT_EXPR)
        {
          PT_ERRORmf (parser, col_expr, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_INDEX_COLUMN,
              pt_short_print (parser, col_expr));
          return;
        }
    }
      /* make sure we don't mix up index types */

      pt_check_function_index_expr (parser, node);
      if (pt_has_error (parser))
    {
      return;
    }

      if (node->info.index.function_expr)
    {
      if (node->info.index.prefix_length || node->info.index.where)
        {
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_CREATE_INDEX);
        }
      return;
    }

      name->info.name.db_object = db_obj;

      /* check that there is only one column to index on */
      pt_check_unique_attr (parser, NULL, node->info.index.column_names, PT_SORT_SPEC);
      if (pt_has_error (parser))
    {
      return;
    }
      pt_check_user_owns_class (parser, name);
      if (pt_has_error (parser))
    {
      return;
    }

      prefix_length = node->info.index.prefix_length;
      if (prefix_length)
    {
      PT_NODE *index_column;

      if (prefix_length->type_enum != PT_TYPE_INTEGER || prefix_length->info.value.data_value.i == 0)
        {
          /*
           * Parser can read PT_TYPE_BIGINT or PT_TYPE_NUMERIC values
           * but domain precision is defined as integer.
           * So, we accept only non-zero values of PT_TYPE_INTEGER.
           */
          PT_ERRORmf (parser, name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_PREFIX_LENGTH,
              prefix_length->info.value.text);
          return;
        }

      assert (node->info.index.column_names != NULL);

      /* check if prefix index is allowed for this type of column */
      for (index_column = node->info.index.column_names; index_column != NULL; index_column = index_column->next)
        {
          PT_NODE *col_dt;

          if (index_column->node_type != PT_SORT_SPEC || index_column->info.sort_spec.expr == NULL
          || index_column->info.sort_spec.expr->node_type != PT_NAME)
        {
          continue;
        }

          col_dt = index_column->info.sort_spec.expr->data_type;

          if (col_dt != NULL && PT_HAS_COLLATION (col_dt->type_enum))
        {
          LANG_COLLATION *lc;

          lc = lang_get_collation (col_dt->info.data_type.collation_id);

          assert (lc != NULL);

          if (!(lc->options.allow_prefix_index))
            {
              PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_PREFIX_LENGTH_COLLATION,
                  index_column->info.sort_spec.expr->info.name.original);
              return;
            }
        }
        }
    }
    }

  /* if this is a filter index, check that the filter is a valid filter expression. */
  pt_check_filter_index_expr (parser, node->info.index.column_names, node->info.index.where, db_obj);
}

static void
pt_check_alter_synonym (PARSER_CONTEXT * parser, PT_NODE * node)
{
  DB_OBJECT *synonym_obj = NULL;
  DB_OBJECT *owner_obj = NULL;
  const char *name = NULL;
  const char *owner_name = NULL;

  if (parser == NULL || node == NULL)
    {
      return;
    }

  assert (node->node_type == PT_ALTER_SYNONYM);

  /* syntax is not supported. */
  assert (PT_SYNONYM_ACCESS_MODIFIER (node) != PT_PUBLIC);

  /* synonym_name */
  name = PT_NAME_ORIGINAL (PT_SYNONYM_NAME (node));
  assert (name != NULL && *name != '\0');
  if (sm_check_system_class_by_name (name) == true)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_INVALID_NAME, name);
      return;
    }

  /* synonym_owner_name */
  owner_name = PT_NAME_ORIGINAL (PT_SYNONYM_OWNER_NAME (node));
  assert (owner_name != NULL && *owner_name != '\0');
  owner_obj = db_find_user (owner_name);
  if (owner_obj == NULL)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_USER_IS_NOT_IN_DB, owner_name);
      return;
    }

  if (ws_is_same_object (owner_obj, Au_user) == false && au_is_dba_group_member (Au_user) == false)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_NOT_OWNER, "ALTER SYNONYM");
      return;
    }

  /* Check if a synonym exists. */
  synonym_obj = db_find_synonym (name);
  if (synonym_obj == NULL)
    {
      ASSERT_ERROR ();

      if (er_errid () == ER_SYNONYM_NOT_EXIST)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_NOT_EXIST, name);
    }

      return;
    }

  /* synonym_obj != NULL */

  if (PT_SYNONYM_TARGET_NAME (node) == NULL)
    {
      /* If only the comment is changed, PT_SYNONYM_TARGET_NAME (node) can be NULL.
       * If both PT_SYNONYM_TARGET_NAME (node) and PT_SYNONYM_COMMENT (node) are NULL,
       * an error occurred in yyparse() and it should not have come here. */
      assert (PT_SYNONYM_COMMENT (node) != NULL);
    }
  else
    {
      /* PT_SYNONYM_TARGET_NAME (node) != NULL */

      /* target_owner_name */
      if (!PT_SYNONYM_IS_DBLINKED (node))
    {
      owner_name = PT_NAME_ORIGINAL (PT_SYNONYM_TARGET_OWNER_NAME (node));
      assert (owner_name != NULL && *owner_name != '\0');
      owner_obj = db_find_user (owner_name);
      if (owner_obj == NULL)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_USER_IS_NOT_IN_DB, owner_name);
          return;
        }
    }
    }
}

static void
pt_check_create_synonym (PARSER_CONTEXT * parser, PT_NODE * node)
{
  DB_OBJECT *synonym_obj = NULL;
  DB_OBJECT *owner_obj = NULL;
  const char *name = NULL;
  const char *owner_name = NULL;

  if (parser == NULL || node == NULL)
    {
      return;
    }

  assert (node->node_type == PT_CREATE_SYNONYM);

  /* syntax is not supported. */
  assert (PT_SYNONYM_ACCESS_MODIFIER (node) != PT_PUBLIC);

  /* synonym_name */
  name = PT_NAME_ORIGINAL (PT_SYNONYM_NAME (node));
  assert (name != NULL && *name != '\0');
  if (sm_check_system_class_by_name (name) == true)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_INVALID_NAME, name);
      return;
    }

  /* synonym_owner_name */
  owner_name = PT_NAME_ORIGINAL (PT_SYNONYM_OWNER_NAME (node));
  assert (owner_name != NULL && *owner_name != '\0');
  owner_obj = db_find_user (owner_name);
  if (owner_obj == NULL)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_USER_IS_NOT_IN_DB, owner_name);
      return;
    }

  if (ws_is_same_object (owner_obj, Au_user) == false && au_is_dba_group_member (Au_user) == false)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_NOT_OWNER, "CREATE SYNONYM");
      return;
    }

  /* or_replace */
  synonym_obj = db_find_synonym (name);
  if (synonym_obj != NULL)
    {
      if (PT_SYNONYM_OR_REPLACE (node) == FALSE)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_ALREADY_EXIST, name);
      return;
    }
    }
  else
    {
      /* synonym_obj == NULL */
      ASSERT_ERROR ();

      if (er_errid () == ER_SYNONYM_NOT_EXIST)
    {
      er_clear ();
    }
      else
    {
      return;
    }

      /* Check if class exists by name. */
      if (db_find_class (name) != NULL)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_EXISTS, name);
      return;
    }
      else
    {
      /* db_find_class () == NULL */
      ASSERT_ERROR ();

      if (er_errid () == ER_LC_UNKNOWN_CLASSNAME)
        {
          er_clear ();
        }
      else
        {
          return;
        }
    }
    }

  /* (synonym_obj != NULL && PT_SYNONYM_OR_REPLACE () == TRUE)
   * || (synonym_obj == NULL && db_find_class () == NULL && er_errid () == ER_LC_UNKNOWN_CLASSNAME) */

  /* target_owner_name */
  if (!PT_SYNONYM_IS_DBLINKED (node))
    {
      owner_name = PT_NAME_ORIGINAL (PT_SYNONYM_TARGET_OWNER_NAME (node));
      assert (owner_name != NULL && *owner_name != '\0');
      owner_obj = db_find_user (owner_name);
      if (owner_obj == NULL)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_USER_IS_NOT_IN_DB, owner_name);
      return;
    }
    }
}

static void
pt_check_drop_synonym (PARSER_CONTEXT * parser, PT_NODE * node)
{
  DB_OBJECT *synonym_obj = NULL;
  DB_OBJECT *owner_obj = NULL;
  const char *name = NULL;
  const char *owner_name = NULL;

  if (parser == NULL || node == NULL)
    {
      return;
    }

  assert (node->node_type == PT_DROP_SYNONYM);

  /* syntax is not supported. */
  assert (PT_SYNONYM_ACCESS_MODIFIER (node) != PT_PUBLIC);

  /* synonym_name */
  name = PT_NAME_ORIGINAL (PT_SYNONYM_NAME (node));
  assert (name != NULL && *name != '\0');
  if (sm_check_system_class_by_name (name) == true)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_INVALID_NAME, name);
      return;
    }

  /* synonym_owner_name */
  owner_name = PT_NAME_ORIGINAL (PT_SYNONYM_OWNER_NAME (node));
  assert (owner_name != NULL && *owner_name != '\0');
  owner_obj = db_find_user (owner_name);
  if (owner_obj == NULL)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_USER_IS_NOT_IN_DB, owner_name);
      return;
    }

  if (ws_is_same_object (owner_obj, Au_user) == false && au_is_dba_group_member (Au_user) == false)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_NOT_OWNER, "DROP SYNONYM");
      return;
    }

  /* if_exists */
  synonym_obj = db_find_synonym (name);
  if (synonym_obj == NULL)
    {
      ASSERT_ERROR ();

      if (er_errid () != ER_SYNONYM_NOT_EXIST)
    {
      return;
    }

      if (PT_SYNONYM_IF_EXISTS (node) == FALSE)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_NOT_EXIST, name);
      return;
    }

      er_clear ();
    }

  /* (synonym_obj != NULL) || (synonym_obj == NULL && PT_SYNONYM_IF_EXISTS () == TRUE) */
}

static void
pt_check_rename_synonym (PARSER_CONTEXT * parser, PT_NODE * node)
{
  DB_OBJECT *old_synonym_obj = NULL;
  DB_OBJECT *new_synonym_obj = NULL;
  DB_OBJECT *old_owner_obj = NULL;
  DB_OBJECT *new_owner_obj = NULL;
  const char *old_name = NULL;
  const char *new_name = NULL;
  const char *old_owner_name = NULL;
  const char *new_owner_name = NULL;

  if (parser == NULL || node == NULL)
    {
      return;
    }

  assert (node->node_type == PT_RENAME_SYNONYM);

  /* syntax is not supported. */
  assert (PT_SYNONYM_ACCESS_MODIFIER (node) != PT_PUBLIC);

  /* old_synonym_name */
  old_name = PT_NAME_ORIGINAL (PT_SYNONYM_OLD_NAME (node));
  assert (old_name != NULL && *old_name != '\0');
  if (sm_check_system_class_by_name (old_name) == true)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_INVALID_NAME, old_name);
      return;
    }

  /* old_synonym_owner_name */
  old_owner_name = PT_NAME_ORIGINAL (PT_SYNONYM_OLD_OWNER_NAME (node));
  assert (old_owner_name != NULL && *old_owner_name != '\0');
  old_owner_obj = db_find_user (old_owner_name);
  if (old_owner_obj == NULL)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_USER_IS_NOT_IN_DB, old_owner_name);
      return;
    }

  if (ws_is_same_object (old_owner_obj, Au_user) == false && au_is_dba_group_member (Au_user) == false)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_NOT_OWNER, "RENAME SYNONYM");
      return;
    }

  old_synonym_obj = db_find_synonym (old_name);
  if (old_synonym_obj == NULL)
    {
      ASSERT_ERROR ();

      if (er_errid () == ER_SYNONYM_NOT_EXIST)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_NOT_EXIST, old_name);
    }

      return;
    }

  /* old_synonym_obj != NULL */

  /* new_synonym_name */
  new_name = PT_NAME_ORIGINAL (PT_SYNONYM_NEW_NAME (node));
  assert (new_name != NULL && *new_name != '\0');
  if (sm_check_system_class_by_name (new_name) == true)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_INVALID_NAME, new_name);
      return;
    }

  /* new_synonym_owner_name */
  new_owner_name = PT_NAME_ORIGINAL (PT_SYNONYM_NEW_OWNER_NAME (node));
  assert (new_owner_name != NULL && *new_owner_name != '\0');
  new_owner_obj = db_find_user (new_owner_name);
  if (new_owner_obj == NULL)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_USER_IS_NOT_IN_DB, new_owner_name);
      return;
    }

  if (ws_is_same_object (old_owner_obj, new_owner_obj) == false)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_RENAME_CANNOT_CHANGE_OWNER);
      return;
    }

  new_synonym_obj = db_find_synonym (new_name);
  if (new_synonym_obj != NULL)
    {
      if (ws_is_same_object (old_synonym_obj, new_synonym_obj) == true)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_RENAME_CANNOT_SAME_NAME);
      return;
    }

      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_ALREADY_EXIST, new_name);
      return;
    }
  else
    {
      /* new_synonym_obj == NULL */
      ASSERT_ERROR ();

      if (er_errid () == ER_SYNONYM_NOT_EXIST)
    {
      er_clear ();
    }
      else
    {
      return;
    }

      /* Check if class exists by name. */
      if (db_find_class (new_name) != NULL)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_EXISTS, new_name);
      return;
    }
      else
    {
      ASSERT_ERROR ();

      if (er_errid () == ER_LC_UNKNOWN_CLASSNAME)
        {
          er_clear ();
        }
      else
        {
          return;
        }
    }
    }

  /* old_synonym_obj != NULL && new_synonym_obj == NULL && er_errid () == ER_LC_UNKNOWN_CLASSNAME */
}

static bool
pt_is_defined_class (PARSER_CONTEXT * parser, PT_NODE * data_type)
{
  if (data_type && data_type->node_type == PT_DATA_TYPE && data_type->type_enum == PT_TYPE_OBJECT)
    {
      PT_NODE *name = data_type->info.data_type.entity;
      if (name && name->node_type == PT_NAME)
    {
      if (!db_find_class (name->info.name.original))
        {
          PT_ERRORmf (parser, data_type, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_DEFINED,
              name->info.name.original);
          return false;
        }
    }
    }

  return true;
}

static PT_TYPE_ENUM
pt_get_type_enum_of_table_column (PARSER_CONTEXT * parser, PT_NODE * data_type)
{
  PT_NODE *table_column = data_type->info.data_type.table_column;
  assert (PT_IS_DOT_NODE (table_column));
  PT_NODE *table_name = table_column->info.dot.arg1;
  PT_NODE *column_name = table_column->info.dot.arg2;
  assert (PT_IS_NAME_NODE (table_name));
  assert (PT_IS_NAME_NODE (column_name));
  const char *table_name_cstr = table_name->info.name.original;
  const char *column_name_cstr = column_name->info.name.original;

  DB_ATTRIBUTE *attr = db_get_attribute_by_name (table_name_cstr, column_name_cstr);
  if (attr == NULL)
    {
      PT_ERRORmf2 (parser, table_column, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UNDEFINED_TABLE_COLUMN,
           table_name_cstr, column_name_cstr);
      return PT_TYPE_NONE;
    }

  DB_DOMAIN *domain = db_attribute_domain (attr);
  assert (domain);
  int db_type = TP_DOMAIN_TYPE (domain);

  return pt_db_to_type_enum ((DB_TYPE) db_type);
}

static bool
pt_is_type_supported_by_sp (PARSER_CONTEXT * parser, PT_TYPE_ENUM & type_enum, PT_NODE * data_type)
{

  if (type_enum == PT_TYPE_NONE && data_type && data_type->type_enum == PT_TYPE_TABLE_COLUMN)
    {
      // type specification of the form <table>.<column>%type
      type_enum = pt_get_type_enum_of_table_column (parser, data_type);
      if (type_enum == PT_TYPE_NONE)
    {
      return false;
    }
    }

  switch (type_enum)
    {
    case PT_TYPE_SMALLINT:
    case PT_TYPE_INTEGER:
    case PT_TYPE_BIGINT:
    case PT_TYPE_NUMERIC:
    case PT_TYPE_FLOAT:
    case PT_TYPE_DOUBLE:
    case PT_TYPE_CHAR:
    case PT_TYPE_VARCHAR:
    case PT_TYPE_DATE:
    case PT_TYPE_TIME:
    case PT_TYPE_DATETIME:
    case PT_TYPE_TIMESTAMP:
    case PT_TYPE_RESULTSET:
      return true;

    case PT_TYPE_OBJECT:
      return pt_is_defined_class (parser, data_type);

    case PT_TYPE_SET:
    case PT_TYPE_MULTISET:
    case PT_TYPE_SEQUENCE:
      {
    PT_NODE *dt;
    for (dt = data_type; dt; dt = dt->next)
      {
        if (!pt_is_defined_class (parser, dt))
          {
        return false;
          }
      }
      }
      return true;

    default:
      return false;
    }
}

static const char *
pt_get_type_name (PT_TYPE_ENUM type_enum, PT_NODE * data_type)
{

  if (type_enum == PT_TYPE_OBJECT)
    {
      if (data_type && data_type->node_type == PT_DATA_TYPE)
    {
      PT_NODE *dt_name = data_type->info.data_type.entity;
      if (dt_name && dt_name->node_type == PT_NAME)
        {
          return dt_name->info.name.original;
        }
    }
    }

  return pt_show_type_enum (type_enum);
}

/*
 * pt_check_default_value_param_for_stored_procedure () - do semantic checks for default value params' invalid form: Out parameter, system expressions, incoercible type
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   node(in): a statement
 */
static int
pt_check_default_value_param_for_stored_procedure (PARSER_CONTEXT * parser, PT_NODE * param)
{
  int error = NO_ERROR;
  PT_NODE *node_ptr = NULL;
  PT_NODE *default_value_node = NULL;
  PT_NODE *default_value = NULL;
  const char *default_value_print = NULL;
  DB_VALUE tmp;

  default_value_node = param->info.sp_param.default_value =
    pt_check_data_default (parser, param->info.sp_param.default_value);
  if (pt_has_error (parser))
    {
      return ER_FAILED;
    }

  assert (default_value_node != NULL && default_value_node->info.data_default.shared == PT_DEFAULT);

  default_value = default_value_node->info.data_default.default_value;
  default_value_print = pt_short_print (parser, default_value);

  if (param->info.sp_param.mode != PT_INPUT && param->info.sp_param.mode != PT_NOPUT)
    {
      PT_ERRORmf (parser,
          param,
          MSGCAT_SET_PARSER_SEMANTIC,
          MSGCAT_SEMANTIC_SP_OUT_DEFAULT_ARG_NOT_ALLOWED, pt_short_print (parser, param->info.sp_param.name));
      return ER_FAILED;
    }

  if (default_value->node_type == PT_EXPR && default_value_node->info.data_default.default_expr_type == DB_DEFAULT_NONE)
    {
      db_make_null (&tmp);
      pt_evaluate_tree (parser, default_value, &tmp, 1);
      if (pt_has_error (parser))
    {
      PT_ERRORmf (parser, default_value, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
              pt_short_print (parser, default_value));
      error = ER_FAILED;
    }
      else
    {
      parser_free_node (parser, default_value_node->info.data_default.default_value);
      default_value_node->info.data_default.default_value = pt_dbval_to_value (parser, &tmp);
    }
      db_value_clear (&tmp);
    }

  if (error == NO_ERROR)
    {
      PT_NODE *dummy = parser_new_node (parser, PT_VALUE);
      error =
    pt_coerce_value_for_default_value (parser, default_value, dummy, param->type_enum, param->data_type,
                       default_value_node->info.data_default.default_expr_type, false);
      parser_free_node (parser, dummy);
      if (error != NO_ERROR)
    {
      PT_ERRORmf2 (parser, default_value, MSGCAT_SET_PARSER_SEMANTIC,
               (error ==
            ER_IT_DATA_OVERFLOW) ? MSGCAT_SEMANTIC_OVERFLOW_COERCING_TO : MSGCAT_SEMANTIC_CANT_COERCE_TO,
               default_value_print, pt_get_type_name (param->type_enum, param->data_type));
    }
    }

  return error;
}

/*
 * pt_check_create_stored_procedure () - do semantic checks on the create procedure/function statement
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   node(in): a statement
 */
static void
pt_check_create_stored_procedure (PARSER_CONTEXT * parser, PT_NODE * node)
{
  int error = NO_ERROR;
  PT_NODE *param = NULL;
  PT_NODE *default_value_node = NULL;
  PT_NODE *default_value = NULL;
  PT_NODE *initial_def_val = NULL;
  int param_count = 0;
  bool has_default_value = false;
  bool is_plcsql = (node->info.sp.body->info.sp_body.lang == SP_LANG_PLCSQL);

  DB_OBJECT *owner = NULL;
  PT_NODE *name = node->info.sp.name;
  const char *owner_name = pt_get_qualifier_name (parser, name);
  if (owner_name)
    {
      owner = db_find_user (owner_name);
      if (owner == NULL)
    {
      ASSERT_ERROR_AND_SET (error);

      if (er_errid () == ER_AU_INVALID_USER)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_USER_IS_NOT_IN_DB, owner_name);
        }
      return;
    }
      else if (au_is_dba_group_member (Au_user) == false && ws_is_same_object (owner, Au_user) == false)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SYNONYM_NOT_OWNER,
              "CREATE PROCEDURE/FUNCTION");
      return;
    }
    }

  for (param = node->info.sp.param_list; param; param = param->next)
    {
      if (param->type_enum == PT_TYPE_NONE)
    {

      // only when the param type is of the form <table>.<column>%TYPE
      assert (param->data_type->type_enum == PT_TYPE_TABLE_COLUMN);

      if (!is_plcsql)
        {
          PT_ERRORm (parser, param, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_ALLOWED_PERCENT_TYPE);
          return;
        }
    }

      if (pt_is_type_supported_by_sp (parser, param->type_enum, param->data_type))
    {
      assert (param->type_enum != PT_TYPE_NONE);
    }
      else
    {
      if (!pt_has_error (parser))
        {
          PT_ERRORmf (parser, param, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_SUPPORTED_SP_ARG_TYPE,
              pt_get_type_name (param->type_enum, param->data_type));
        }
      goto end;
    }

      /* check trailing arguments */
      if (param->info.sp_param.default_value != NULL)
    {
      has_default_value = true;
      // check default value
      error = pt_check_default_value_param_for_stored_procedure (parser, param);
      if (error != NO_ERROR)
        {
          goto end;
        }
    }
      else
    {
      // error for non-trailing arguments
      if (has_default_value)
        {
          PT_ERRORmf (parser,
              param,
              MSGCAT_SET_PARSER_SEMANTIC,
              MSGCAT_SEMANTIC_SP_NON_TRAILING_OPTIONAL_PARAMS,
              pt_short_print (parser, param->info.sp_param.name));
          goto end;
        }
    }
    }

  if (node->info.sp.type == PT_SP_FUNCTION)
    {
      if (node->info.sp.ret_type == PT_TYPE_NONE)
    {

      // only when the return type is of the form <table>.<column>%TYPE
      assert (node->info.sp.ret_data_type->type_enum == PT_TYPE_TABLE_COLUMN);

      if (!is_plcsql)
        {
          PT_ERRORm (parser, node->info.sp.ret_data_type, MSGCAT_SET_PARSER_SEMANTIC,
             MSGCAT_SEMANTIC_NOT_ALLOWED_PERCENT_TYPE);
          return;
        }
    }

      if (pt_is_type_supported_by_sp (parser, node->info.sp.ret_type, node->info.sp.ret_data_type))
    {
      assert (node->info.sp.ret_type != PT_TYPE_NONE);
    }
      else
    {
      if (!pt_has_error (parser))
        {
          PT_ERRORmf (parser, node->info.sp.ret_data_type, MSGCAT_SET_PARSER_SEMANTIC,
              MSGCAT_SEMANTIC_NOT_SUPPORTED_SP_RET_TYPE, pt_get_type_name (node->info.sp.ret_type,
                                               node->info.sp.ret_data_type));
        }
      goto end;
    }
    }

end:
  return;
}

/*
 * pt_check_drop () - do semantic checks on the drop statement
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   node(in): a statement
 */
static void
pt_check_drop (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *temp;
  PT_NODE *name;
  const char *entity_name;
  DB_OBJECT *db_obj;
  DB_ATTRIBUTE *attributes;
  PT_FLAT_SPEC_INFO info;
#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
  DB_OBJECT *domain_class;
  char *drop_name_list = NULL;
#endif /* ENABLE_UNUSED_FUNCTION */

  if (node->info.drop.if_exists)
    {
      PT_NODE *prev_node, *free_node, *tmp1, *tmp2;
      prev_node = free_node = node->info.drop.spec_list;

      while ((free_node != NULL) && (free_node->node_type == PT_SPEC))
    {
      /* check if class name exists. if not, we remove the corresponding node from spec_list. */
      if ((name = free_node->info.spec.entity_name) != NULL && name->node_type == PT_NAME
          && (entity_name = name->info.name.original) != NULL)
        {
          /* We cannot change the schema of a class by using synonym names. */
          if (db_find_synonym (entity_name) == NULL)
        {
          ASSERT_ERROR ();

          if (er_errid () == ER_SYNONYM_NOT_EXIST)
            {
              er_clear ();
            }
          else
            {
              return;
            }

          if ((db_obj = db_find_class_with_purpose (entity_name, true)) != NULL)
            {
              prev_node = free_node;
              free_node = free_node->next;

              continue;
            }
        }

          /* db_find_synonym () != NULL || db_find_class_with_purpose () == NULL */
          if (free_node == node->info.drop.spec_list)
        {
          node->info.drop.spec_list = node->info.drop.spec_list->next;
          prev_node = free_node->next;
          parser_free_node (parser, free_node);
          free_node = node->info.drop.spec_list;
        }
          else
        {
          prev_node->next = free_node->next;
          parser_free_node (parser, free_node);
          free_node = prev_node->next;
        }
        }
      else
        {
          prev_node = free_node;
          free_node = free_node->next;
        }
    }
      /* For each class, we check if it has previously been placed in spec_list. We also check if every class has a
       * superclass marked with PT_ALL previously placed in spec_list. If any of the two cases above occurs, we mark
       * for deletion the corresponding node in spec_list. Marking is done by setting the entity_name as NULL. */

      if (node->info.drop.spec_list && (node->info.drop.spec_list)->next)
    {
      tmp1 = (node->info.drop.spec_list)->next;
      while ((tmp1 != NULL) && (tmp1->node_type == PT_SPEC))
        {
          tmp2 = node->info.drop.spec_list;
          while ((tmp2 != NULL) && (tmp2 != tmp1) && (tmp2->node_type == PT_SPEC))
        {
          DB_OBJECT *db_obj1, *db_obj2;
          PT_NODE *name1, *name2;
          const char *cls_name1, *cls_name2;

          name1 = tmp1->info.spec.entity_name;
          name2 = tmp2->info.spec.entity_name;
          if (name1 && name2)
            {
              cls_name1 = name1->info.name.original;
              cls_name2 = name2->info.name.original;

              db_obj1 = db_find_class_with_purpose (cls_name1, true);
              db_obj2 = db_find_class_with_purpose (cls_name2, true);

              if ((db_obj1 == db_obj2)
              || ((tmp2->info.spec.only_all == PT_ALL) && db_is_subclass (db_obj1, db_obj2)))
            {
              parser_free_node (parser, name1);
              tmp1->info.spec.entity_name = NULL;
              break;
            }
            }

          tmp2 = tmp2->next;
        }

          tmp1 = tmp1->next;
        }
    }

      /* now we remove the nodes with entity_name NULL */

      prev_node = free_node = node->info.drop.spec_list;

      while ((free_node != NULL) && (free_node->node_type == PT_SPEC))
    {
      if ((name = free_node->info.spec.entity_name) == NULL)
        {
          if (free_node == node->info.drop.spec_list)
        {
          node->info.drop.spec_list = node->info.drop.spec_list->next;
          prev_node = free_node->next;
          parser_free_node (parser, free_node);
          free_node = node->info.drop.spec_list;
        }
          else
        {
          prev_node->next = free_node->next;
          parser_free_node (parser, free_node);
          free_node = prev_node->next;
        }
        }
      else
        {
          prev_node = free_node;
          free_node = free_node->next;
        }
    }
    }
  else
    {
      for (temp = node->info.drop.spec_list; temp && temp->node_type == PT_SPEC; temp = temp->next)
    {
      if ((name = temp->info.spec.entity_name) != NULL && name->node_type == PT_NAME
          && (entity_name = name->info.name.original) != NULL)
        {
          /* We cannot change the schema of a class by using synonym names. */
          if (db_find_synonym (entity_name) != NULL)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_EXIST,
                  entity_name);
          return;
        }
          else
        {
          /* db_find_synonym () == NULL */
          ASSERT_ERROR ();

          if (er_errid () == ER_SYNONYM_NOT_EXIST)
            {
              er_clear ();
            }
          else
            {
              return;
            }
        }
        }
    }
    }

  info.spec_parent = NULL;
  info.for_update = true;
  /* Replace each Entity Spec with an Equivalent flat list */
  parser_walk_tree (parser, node, pt_flat_spec_pre, &info, pt_continue_walk, NULL);

  if (node->info.drop.entity_type != PT_MISC_DUMMY || node->info.drop.is_cascade_constraints)
    {
      PT_MISC_TYPE typ = node->info.drop.entity_type;

      /* verify declared class type is correct */
      for (temp = node->info.drop.spec_list; temp && temp->node_type == PT_SPEC; temp = temp->next)
    {
      if ((name = temp->info.spec.entity_name) != NULL && name->node_type == PT_NAME
          && (entity_name = name->info.name.original) != NULL && (db_obj = db_find_class (entity_name)) != NULL)
        {
          if (typ != PT_MISC_DUMMY)
        {
          name->info.name.db_object = db_obj;
          pt_check_user_owns_class (parser, name);
          if ((typ == PT_CLASS && db_is_class (db_obj) <= 0)
              || (typ == PT_VCLASS && db_is_vclass (db_obj) <= 0))
            {
              PT_ERRORmf2 (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_A, entity_name,
                   pt_show_misc_type (typ));
            }
        }

          if (node->info.drop.is_cascade_constraints && db_is_vclass (db_obj) > 0)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC,
                  MSGCAT_SEMANTIC_VIEW_CASCADE_CONSTRAINTS_NOT_ALLOWED, entity_name);
        }
        }
    }
    }

  /* for the classes to drop, check if a TEXT attr is defined on the class, and if defined, drop the reference class
   * for the attr */
  for (temp = node->info.drop.spec_list; temp && temp->node_type == PT_SPEC; temp = temp->next)
    {
      if ((name = temp->info.spec.entity_name) != NULL && name->node_type == PT_NAME
      && (entity_name = name->info.name.original) != NULL && (db_obj = db_find_class (entity_name)) != NULL)
    {
      attributes = db_get_attributes_force (db_obj);
      while (attributes)
        {
          if (db_attribute_type (attributes) == DB_TYPE_OBJECT)
        {
#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
          if (sm_has_text_domain (attributes, 0))
            {
              domain_class = db_domain_class (db_attribute_domain (attributes));
              if (drop_name_list != NULL)
            {
              drop_name_list = pt_append_string (parser, drop_name_list, ",");
            }

              drop_name_list = pt_append_string (parser, drop_name_list, db_get_class_name (domain_class));
            }
#endif /* ENABLE_UNUSED_FUNCTION */
        }
          attributes = db_attribute_next (attributes);
        }
    }
    }

#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
  if (drop_name_list)
    {
      node->info.drop.internal_stmts =
    pt_append_statements_on_drop_attributes (parser, node->info.drop.internal_stmts, drop_name_list);
      if (node->info.drop.internal_stmts == NULL)
    {
      PT_ERRORm (parser, temp, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      return;
    }
    }
#endif /* ENABLE_UNUSED_FUNCTION */
}

/*
 * pt_check_grant_revoke () - do semantic checks on the grant statement
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   node(in): a statement
 */

static void
pt_check_grant_revoke (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *user;
  const char *username;
  PT_FLAT_SPEC_INFO info;

  bool is_for_spec = true;
  PT_NODE *auth_cmd_list = node->info.grant.auth_cmd_list;
  while (auth_cmd_list)
    {
      PT_PRIV_TYPE pt_auth = auth_cmd_list->info.auth_cmd.auth_cmd;
      if (pt_auth == PT_EXECUTE_PROCEDURE_PRIV)
    {
      is_for_spec = false;
      break;
    }

      auth_cmd_list = auth_cmd_list->next;
    }

  if (is_for_spec == true)
    {
      /* Replace each Entity Spec with an Equivalent flat list */
      info.spec_parent = NULL;
      info.for_update = false;
      parser_walk_tree (parser, node, pt_flat_spec_pre, &info, pt_continue_walk, NULL);
    }
  else
    {
      /* check grant option */
      if (node->info.grant.grant_option == PT_GRANT_OPTION)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_AU_GRANT_OPTION_NOT_ALLOWED,
              MSGCAT_GET_GLOSSARY_MSG (MSGCAT_GLOSSARY_PROCEDURE));
    }

      /* check spec_list (procedures/functions) exists */
      for (PT_NODE * procs = node->info.grant.spec_list; procs != NULL; procs = procs->next)
    {
      // [TODO] Resovle user schema name, built-in package name
      const char *proc_name = procs->info.name.original;
      if (jsp_is_exist_stored_procedure (proc_name) == false)
        {
          PT_ERRORmf (parser, procs, MSGCAT_SET_PARSER_SEMANTIC, MSTCAT_SEMANTIC_SP_NOT_EXIST, proc_name);
          break;
        }
    }
    }

  /* make sure the grantees/revokees exist */
  for ((user = (node->node_type == PT_GRANT ? node->info.grant.user_list : node->info.revoke.user_list)); user;
       user = user->next)
    {
      if (user->node_type == PT_NAME && (username = user->info.name.original) && !db_find_user (username))
    {
      PT_ERRORmf (parser, user, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_USER_IS_NOT_IN_DB, username);
    }
    }
}

/*
 * pt_check_method () - semantic checking for method calls in expressions.
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   node(in): a statement
 */

static void
pt_check_method (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *target;
  DB_VALUE val;
  const char *method_name;
  DB_OBJECT *class_op;
  DB_METHOD *method = NULL;

  /* Note: it is possible that an error has been raised eariler. An example is that, method_name is NULL due to a
   * connection lost, i.e. db_Connect_status is 0, 'MSGCAT_SEMANTIC_METH_NO_TARGET' is set. So we must check error
   * first. */

  assert (parser != NULL);

  if (pt_has_error (parser))
    {
      return;
    }

  assert (node != NULL && node->info.method_call.method_name != NULL);

  db_make_null (&val);

  /* check if call has a target */
  target = node->info.method_call.on_call_target;
  if (target == NULL)
    {
      if (jsp_is_exist_stored_procedure (node->info.method_call.method_name->info.name.original))
    {
      return;
    }
      else
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_METH_NO_TARGET);
      return;
    }
    }

  /* if we have a null target, constant folding has determined we have no target, there is nothing to check. */
  if ((target->node_type == PT_VALUE) && (target->type_enum == PT_TYPE_NULL))
    {
      return;
    }

  if ((!target->data_type) || (!target->data_type->info.data_type.entity))
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_METH_TARGET_NOT_OBJ);
      return;
    }

  if (!(class_op = target->data_type->info.data_type.entity->info.name.db_object))
    {
      PT_INTERNAL_ERROR (parser, "semantic");
      return;
    }

  method_name = node->info.method_call.method_name->info.name.original;
  method = (DB_METHOD *) db_get_method (class_op, method_name);
  if (method == NULL)
    {
      if (er_errid () == ER_OBJ_INVALID_METHOD)
    {
      er_clear ();
    }

      method = (DB_METHOD *) db_get_class_method (class_op, method_name);
      if (method == NULL)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_METH_DOESNT_EXIST, method_name);
      return;
    }
      else
    {
      /* Check to see that they are calling the class method on a class object.  We check the initial value if it
       * is a parameter knowing that the user could change it at run time.  We probably need to add runtime checks
       * if we are not already doing this. Also we probably shouldn't get PT_VALUES here now that parameters are
       * not bound until runtime (but we'll guard against them anyway). */
      if (((target->node_type != PT_NAME) || (target->info.name.meta_class != PT_PARAMETER)
           || !pt_eval_path_expr (parser, target, &val) || db_is_instance (db_get_object (&val)) <= 0)
          && target->node_type != PT_VALUE
          && (target->data_type->info.data_type.entity->info.name.meta_class != PT_META_CLASS))
        {
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_METH_MIX_CLASS_INST);
          return;
        }
    }
    }
  else
    {
      /* Check to see that they are calling the instance method on an instance object.  We check the initial value if
       * it is a parameter knowing that the user could change it at run time.  We probably need to add runtime checks
       * if we are not already doing this. Also we probably shouldn't get PT_VALUES here now that parameters are not
       * bound until runtime (but we'll guard against them anyway). */
      if (((target->node_type != PT_NAME) || (target->info.name.meta_class != PT_PARAMETER)
       || !pt_eval_path_expr (parser, target, &val) || db_is_instance (db_get_object (&val)) <= 0)
      && target->node_type != PT_VALUE
      && (target->data_type->info.data_type.entity->info.name.meta_class != PT_CLASS))
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_METH_MIX_INST_CLASS);
      return;
    }
    }

  /* check if number of parameters match */
  if (db_method_arg_count (method) != pt_length_of_list (node->info.method_call.arg_list))
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_METH_ARG_NE_DEFINED);
      return;
    }

}

/*
 * pt_check_truncate () - do semantic checks on the truncate statement
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   node(in): a statement
 */
static void
pt_check_truncate (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *temp;
  PT_NODE *name;
  PT_FLAT_SPEC_INFO info;
  DB_OBJECT *db_obj;

  /* replace entity spec with an equivalent flat list */
  info.spec_parent = NULL;
  info.for_update = false;
  parser_walk_tree (parser, node, pt_flat_spec_pre, &info, pt_continue_walk, NULL);

  temp = node->info.truncate.spec;
  if (temp && temp->node_type == PT_SPEC)
    {
      const char *cls_nam;

      if ((name = temp->info.spec.entity_name) != NULL && name->node_type == PT_NAME
      && (cls_nam = name->info.name.original) != NULL)
    {
      /* We cannot change the schema of a class by using synonym names. */
      if (db_find_synonym (cls_nam) != NULL)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_EXIST, cls_nam);
          return;
        }
      else
        {
          /* db_find_synonym () == NULL */
          ASSERT_ERROR ();

          if (er_errid () == ER_SYNONYM_NOT_EXIST)
        {
          er_clear ();
        }
          else
        {
          return;
        }
        }

      if ((db_obj = db_find_class (cls_nam)) != NULL)
        {
          name->info.name.db_object = db_obj;
          pt_check_user_owns_class (parser, name);
          if (db_is_class (db_obj) <= 0)
        {
          PT_ERRORmf2 (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_A, cls_nam,
                   pt_show_misc_type (PT_CLASS));
        }
        }
    }
    }
}

/*
 * pt_check_kill () - do semantic checks on the kill statement
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   node(in): a statement
 */
static void
pt_check_kill (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *tran_id;

  for (tran_id = node->info.killstmt.tran_id_list; tran_id != NULL; tran_id = tran_id->next)
    {
      if (tran_id->type_enum != PT_TYPE_INTEGER)
    {
      PT_ERRORm (parser, tran_id, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_TRANSACTION_ID_WANT_INTEGER);
      break;
    }
    }
}

/*
 * pt_check_alter_serial () - do semantic checks on the alter serial statement
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   node(in): a statement
 */
static void
pt_check_alter_serial (PARSER_CONTEXT * parser, PT_NODE * node)
{
  DB_OBJECT *serial_class = NULL, *serial_object = NULL;
  DB_IDENTIFIER serial_obj_id;
  DB_OBJECT *owner_object = NULL;
  PT_NODE *start_val = NULL;
  PT_NODE *increment_val = NULL;
  PT_NODE *max_val = NULL;
  PT_NODE *min_val = NULL;
  int cyclic;
  int no_max;
  int no_min;
  int no_cyclic;
  PT_NODE *cached_num_val = NULL;
  int no_cache;
  const char *name = NULL;
  const char *owner_name = NULL;

  OID_SET_NULL (&serial_obj_id);

  assert (node->node_type == PT_ALTER_SERIAL);

  /* find _db_serial class */
  serial_class = sm_find_class (CT_SERIAL_NAME);
  if (serial_class == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_DB_SERIAL_NOT_FOUND, 0);
      return;
    }

  /* serial_name */
  name = node->info.serial.serial_name->info.name.original;
  if (name == NULL)
    {
      assert (sm_check_system_class_by_name (name));
    }

  /* Check if a serial object exists. */
  serial_object = do_get_serial_obj_id (&serial_obj_id, serial_class, name);
  if (serial_object == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_SERIAL_NOT_FOUND, 1, name);
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RT_SERIAL_NOT_DEFINED, name);
      return;
    }

  switch (node->info.serial.code)
    {
    case PT_SERIAL_OPTION:
      start_val = node->info.serial.start_val;
      increment_val = node->info.serial.increment_val;
      max_val = node->info.serial.max_val;
      min_val = node->info.serial.min_val;
      cyclic = node->info.serial.cyclic;
      no_max = node->info.serial.no_max;
      no_min = node->info.serial.no_min;
      no_cyclic = node->info.serial.no_cyclic;
      cached_num_val = node->info.serial.cached_num_val;
      no_cache = node->info.serial.no_cache;

      if (!start_val && !increment_val && !max_val && !min_val
      && cyclic == 0 && no_max == 0 && no_min == 0
      && no_cyclic == 0 && !cached_num_val && no_cache == 0 && node->info.serial.comment == NULL)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SERIAL_ALTER_NO_OPTION, 0);
      return;
    }
      break;

    case PT_CHANGE_OWNER:
      if (node->info.serial.owner_name == NULL && node->info.serial.comment == NULL)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SERIAL_ALTER_NO_OPTION, 0);
      return;
    }

      /* serial_owner_name */
      owner_name = node->info.serial.owner_name->info.name.original;
      assert (owner_name != NULL && *owner_name != '\0');
      owner_object = db_find_user (owner_name);
      if (owner_object == NULL)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_USER_IS_NOT_IN_DB, owner_name);
      return;
    }

      if (au_is_dba_group_member (Au_user) == false)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SERIAL_NOT_OWNER);
      return;
    }
      break;

    default:
      assert (false);
      break;
    }
}

/*
 * pt_check_update_stats () - do semantic checks on the UPDATE STATISTICS statement
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   node(in): a statement
 */
static void
pt_check_update_stats (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *class_name_node;
  PT_FLAT_SPEC_INFO info;

  assert (node->node_type == PT_UPDATE_STATS);

  if (node->info.update_stats.all_classes != 0)
    {
      /* The following UPDATE STATISTICS statements do not need to be checked because class names are not used.
       *   - UPDATE STATISTICS ON ALL CLASSES     : (PT_NODE *)->info.update_stats.all_classes == 1
       *   - UPDATE STATISTICS ON CATALOG CLASSES : (PT_NODE *)->info.update_stats.all_classes == -1
       */
      return;
    }

  /* replace entity spec with an equivalent flat list */
  info.spec_parent = NULL;
  info.for_update = false;
  parser_walk_tree (parser, node, pt_flat_spec_pre, &info, pt_continue_walk, NULL);

  for (class_name_node = node->info.update_stats.class_list; class_name_node != NULL;
       class_name_node = class_name_node->next)
    {
      assert (class_name_node->node_type == PT_NAME);
      assert (class_name_node->info.name.original != NULL);

      const char *class_name = class_name_node->info.name.original;

      /* The use of synonyms is not allowed in the update statistics statement. */
      if (db_find_synonym (class_name) != NULL)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_EXIST, class_name);
      return;
    }
      else
    {
      /* db_find_synonym () == NULL */
      ASSERT_ERROR ();

      if (er_errid () == ER_SYNONYM_NOT_EXIST)
        {
          er_clear ();
        }
      else
        {
          return;
        }
    }
    }
}

/*
 * pt_check_single_valued_node () - looks for names outside an aggregate
 *      which are not in group by list. If it finds one, raises an error
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in/out):
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_check_single_valued_node (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_AGG_CHECK_INFO *info = (PT_AGG_CHECK_INFO *) arg;
  PT_NODE *spec, *arg2, *group, *expr;
  char *node_str;

  *continue_walk = PT_CONTINUE_WALK;

  if (pt_is_aggregate_function (parser, node))
    {
      *continue_walk = PT_LIST_WALK;
    }
  else if (node->node_type == PT_SELECT)
    {
      /* 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)
    {
      (void) parser_walk_tree (parser, node->next, pt_check_single_valued_node, info,
                   pt_check_single_valued_node_post, info);
    }

      /* don't get confused by uncorrelated, set-derived subqueries. */
      if (node->info.query.correlation_level == 0 && (spec = node->info.query.q.select.from)
      && spec->info.spec.derived_table && spec->info.spec.derived_table_type == PT_IS_SET_EXPR)
    {
      /* no need to dive into the uncorrelated subquery */
      *continue_walk = PT_STOP_WALK;
    }
      else
    {
      *continue_walk = PT_LEAF_WALK;
    }

      /* increase query depth as we dive into subqueries */
      info->depth++;
    }
  else
    {
      switch (node->node_type)
    {
    case PT_NAME:
      *continue_walk = PT_LIST_WALK;

      if (pt_find_spec (parser, info->from, node) && pt_find_attribute (parser, node, info->group_by) < 0)
        {
          if ((!PT_IS_OID_NAME (node) || parser->oid_included != PT_INCLUDE_OID_TRUSTME)
          && !PT_IS_CLASSOID_NAME (node) && node->info.name.meta_class != PT_METHOD
          && node->info.name.meta_class != PT_META_ATTR && node->info.name.meta_class != PT_META_CLASS
          && node->info.name.meta_class != PT_PARAMETER)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_SINGLE_VALUED,
                  pt_short_print (parser, node));
        }
        }
      break;

    case PT_DOT_:
      *continue_walk = PT_LIST_WALK;

      if ((arg2 = node->info.dot.arg2) && pt_find_spec (parser, info->from, arg2)
          && (pt_find_attribute (parser, arg2, info->group_by) < 0))
        {
          if (!PT_IS_OID_NAME (node->info.dot.arg2) || parser->oid_included != PT_INCLUDE_OID_TRUSTME)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_SINGLE_VALUED,
                  pt_short_print (parser, node));
        }
        }
      break;

    case PT_VALUE:
      /* watch out for parameters of type object--don't walk their data_type list */
      *continue_walk = PT_LIST_WALK;
      break;

    case PT_EXPR:
      if (node->info.expr.op == PT_INST_NUM || node->info.expr.op == PT_ROWNUM || node->info.expr.op == PT_PRIOR
          || node->info.expr.op == PT_CONNECT_BY_ROOT || node->info.expr.op == PT_SYS_CONNECT_BY_PATH)
        {
          if (info->depth == 0)
        {       /* not in subqueries */
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_SINGLE_VALUED,
                  pt_short_print (parser, node));
        }
        }
      else if (node->info.expr.op == PT_LEVEL || node->info.expr.op == PT_CONNECT_BY_ISCYCLE
           || node->info.expr.op == PT_CONNECT_BY_ISLEAF)
        {
          if (info->depth == 0)
        {       /* not in subqueries */
          for (group = info->group_by; group; group = group->next)
            {
              expr = group->info.sort_spec.expr;
              if (expr->node_type == PT_EXPR && expr->info.expr.op == node->info.expr.op)
            {
              break;
            }
            }
          if (group == NULL)
            {
              PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_SINGLE_VALUED,
                  pt_short_print (parser, node));
            }
        }
        }
      else
        {
          unsigned int save_custom;

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

          parser->custom_print |= PT_CONVERT_RANGE;
          node_str = parser_print_tree (parser, node);

          for (group = info->group_by; group; group = group->next)
        {
          expr = group->info.sort_spec.expr;
          if (expr->node_type == PT_EXPR && pt_str_compare (node_str, expr->alias_print, CASE_INSENSITIVE) == 0)
            {
              /* find matched expression */
              *continue_walk = PT_LIST_WALK;
              break;
            }
        }

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

    default:
      break;
    }
    }

  return node;
}

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

  *continue_walk = PT_CONTINUE_WALK;

  if (node->node_type == PT_SELECT)
    {
      info->depth--;        /* decrease query depth */
    }

  return node;
}

/*
 * pt_check_into_clause_for_static_sql ()
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   qry(in): a SELECT/UNION/INTERSECTION/DIFFERENCE statement
 */
static void
pt_check_into_clause_for_static_sql (PARSER_CONTEXT * parser, PT_NODE * qry, int into_cnt)
{
  // set external into labels in parser context
  PT_NODE *into = qry->info.query.into_list;

  char **external_into_label = (char **) malloc (into_cnt * sizeof (char *));
  for (int i = 0; i < into_cnt; i++)
    {
      external_into_label[i] = (char *) malloc (sizeof (char) * 255);
      strncpy (external_into_label[i], into->info.name.original, 254);
      into = into->next;
    }
  parser->external_into_label_cnt = into_cnt;
  parser->external_into_label = external_into_label;

  parser_free_tree (parser, qry->info.query.into_list);
  qry->info.query.into_list = NULL;
}

/*
 * pt_check_into_clause () - check arity of any into_clause
 *                           equals arity of query
 *   return:  none
 *   parser(in): the parser context used to derive the statement
 *   qry(in): a SELECT/UNION/INTERSECTION/DIFFERENCE statement
 */
static void
pt_check_into_clause (PARSER_CONTEXT * parser, PT_NODE * qry)
{
  PT_NODE *into;
  int tgt_cnt, col_cnt;

  assert (parser != NULL);

  if (!qry)
    return;

  if (!(into = qry->info.query.into_list))
    return;

  if (qry->info.query.is_subquery != 0)
    {
      /* current query execution mechanism can not handle select_into inside subqueries, since only the results of
       * the main query are available on the client, where the select_into assignment is done
       */
      PT_ERRORm (parser, qry, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SELECT_INTO_IN_SUBQUERY);
    }

  tgt_cnt = pt_length_of_list (into);
  col_cnt = pt_length_of_select_list (pt_get_select_list (parser, qry), EXCLUDE_HIDDEN_COLUMNS);
  if (tgt_cnt != col_cnt)
    {
      if (parser->flag.is_parsing_static_sql == 1 && tgt_cnt == 1)
    {
      // OK. the single target can be a record
    }
      else
    {
      PT_ERRORmf2 (parser, into, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_COL_CNT_NE_INTO_CNT, col_cnt, tgt_cnt);
    }
    }

  if (parser->flag.is_parsing_static_sql == 1)
    {
      pt_check_into_clause_for_static_sql (parser, qry, tgt_cnt);
    }
}

static int
pt_normalize_path (PARSER_CONTEXT * parser, REFPTR (char, c))
{
  std::string normalized_path;

  int error_code = db_json_normalize_path_string (c, normalized_path);
  if (error_code != NO_ERROR)
    {
      return error_code;
    }
  char *normalized = (char *) parser_alloc (parser, (int) (normalized_path.length () + 1) * sizeof (char));
  strcpy (normalized, normalized_path.c_str ());
  c = normalized;
  return NO_ERROR;
}

static int
pt_json_str_codeset_normalization (PARSER_CONTEXT * parser, REFPTR (char, c))
{
  DB_VALUE res_str, temp;
  db_make_string (&temp, c);

  int error_code = db_string_convert_to (&temp, &res_str, INTL_CODESET_UTF8, LANG_COLL_UTF8_BINARY);
  if (error_code != NO_ERROR)
    {
      pr_clear_value (&res_str);
      ASSERT_ERROR ();
      return error_code;
    }

  c = (char *) parser_alloc (parser, db_get_string_size (&res_str));
  strcpy (c, db_get_string (&res_str));

  pr_clear_value (&res_str);
  return NO_ERROR;
}

/*
 * pt_check_json_table_node () - check json_table's paths and type check ON_ERROR & ON_EMPTY
 *
 *   return:  NO_ERROR or ER_JSON_INVALID_PATH
 *   node(in): json_table node
 */
static int
pt_check_json_table_node (PARSER_CONTEXT * parser, PT_NODE * node)
{
  assert (node != NULL && node->node_type == PT_JSON_TABLE_NODE);

  int error_code = pt_json_str_codeset_normalization (parser, node->info.json_table_node_info.path);
  if (error_code != NO_ERROR)
    {
      return error_code;
    }

  error_code = pt_normalize_path (parser, node->info.json_table_node_info.path);
  if (error_code != NO_ERROR)
    {
      return error_code;
    }

  for (PT_NODE * col = node->info.json_table_node_info.columns; col; col = col->next)
    {
      PT_JSON_TABLE_COLUMN_INFO & col_info = col->info.json_table_column_info;
      if (col_info.on_empty.m_behavior == JSON_TABLE_DEFAULT_VALUE)
    {
      assert (col_info.on_empty.m_default_value != NULL);

      if (DB_IS_STRING (col_info.on_empty.m_default_value))
        {
          error_code = db_json_convert_to_utf8 (col_info.on_empty.m_default_value);
          if (error_code != NO_ERROR)
        {
          return NO_ERROR;
        }
        }

      TP_DOMAIN *domain = pt_xasl_node_to_domain (parser, col);
      TP_DOMAIN_STATUS status_cast =
        tp_value_cast (col_info.on_empty.m_default_value, col_info.on_empty.m_default_value, domain, false);
      if (status_cast != DOMAIN_COMPATIBLE)
        {
          return tp_domain_status_er_set (status_cast, ARG_FILE_LINE, col_info.on_empty.m_default_value, domain);
        }
    }

      if (col_info.on_error.m_behavior == JSON_TABLE_DEFAULT_VALUE)
    {
      assert (col_info.on_error.m_default_value != NULL);

      if (DB_IS_STRING (col_info.on_error.m_default_value))
        {
          error_code = db_json_convert_to_utf8 (col_info.on_error.m_default_value);
          if (error_code != NO_ERROR)
        {
          return NO_ERROR;
        }
        }

      TP_DOMAIN *domain = pt_xasl_node_to_domain (parser, col);
      TP_DOMAIN_STATUS status_cast =
        tp_value_cast (col_info.on_error.m_default_value, col_info.on_error.m_default_value, domain, false);
      if (status_cast != DOMAIN_COMPATIBLE)
        {
          return tp_domain_status_er_set (status_cast, ARG_FILE_LINE, col_info.on_error.m_default_value, domain);
        }
    }

      if (col_info.func == JSON_TABLE_ORDINALITY)
    {
      // ORDINALITY columns do not have a path
      assert (col_info.path == NULL);
      continue;
    }
      error_code = pt_normalize_path (parser, col_info.path);
      if (error_code)
    {
      return error_code;
    }
    }

  for (PT_NODE * nested_col = node->info.json_table_node_info.nested_paths; nested_col; nested_col = nested_col->next)
    {
      error_code = pt_check_json_table_node (parser, nested_col);
      if (error_code)
    {
      return error_code;
    }
    }
  return NO_ERROR;
}

/*
 * pt_semantic_check_local () - checks semantics on a particular statement
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
pt_semantic_check_local (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  SEMANTIC_CHK_INFO *info = (SEMANTIC_CHK_INFO *) arg;
  PT_NODE *next, *top_node = info->top_node;
  PT_NODE *orig = node;
  PT_NODE *t_node;
  PT_NODE *entity, *derived_table;
  PT_ASSIGNMENTS_HELPER ea;
  STATEMENT_SET_FOLD fold_as;
  PT_FUNCTION_INFO *func_info_p = NULL;

  assert (parser != NULL);

  if (!node)
    return NULL;

  next = node->next;
  node->next = NULL;

  switch (node->node_type)
    {
      /* Every type of node that can appear at the highest level should be listed here, unless no semantic check is
       * required. */
    case PT_DELETE:
      if (top_node->flag.cannot_prepare == 1)
    {
      node->flag.cannot_prepare = 1;
    }

      entity = NULL;

      /* Make sure that none of the classes that are subject for delete is a derived table */
      t_node = node->info.delete_.target_classes;

      if (t_node == NULL)
    {
      /* this is not a multi-table delete; check all specs for derived tables */
      entity = node->info.delete_.spec;

      while (entity)
        {
          assert (entity->node_type == PT_SPEC);

          if (entity->info.spec.derived_table != NULL)
        {
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DELETE_DERIVED_TABLE);
          break;
        }
        }
    }
      else
    {
      /* multi-table delete */
      while (t_node)
        {
          entity = pt_find_spec_in_statement (parser, node, t_node);

          if (entity == NULL)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_RESOLUTION_FAILED,
                  t_node->info.name.original);
          break;
        }

          if (entity->info.spec.derived_table != NULL)
        {
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DELETE_DERIVED_TABLE);
          break;
        }

          t_node = t_node->next;
        }
    }

      node = pt_semantic_type (parser, node, info);
      break;

    case PT_INSERT:
      if (top_node && top_node->flag.cannot_prepare == 1)
    {
      node->flag.cannot_prepare = 1;
    }

      if (node->info.insert.into_var != NULL && node->info.insert.value_clauses->next != NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DO_INSERT_TOO_MANY, 0);
      if (!pt_has_error (parser))
        {
          PT_ERRORc (parser, node, db_error_string (3));
        }
      break;
    }

      if (node->info.insert.odku_assignments != NULL)
    {
      node = pt_check_odku_assignments (parser, node);
      if (node == NULL || pt_has_error (parser))
        {
          break;
        }
    }
      /* semantic check value clause for SELECT and INSERT subclauses */
      if (node)
    {
      node = pt_semantic_type (parser, node, info);
    }

      /* try to coerce insert_values into types indicated by insert_attributes */
      if (node && node->info.insert.spec && node->info.insert.spec->info.spec.remote_server_name == NULL)
    {
      pt_coerce_insert_values (parser, node);
    }

      if (pt_has_error (parser))
    {
      break;
    }

      if (node->info.insert.value_clauses->info.node_list.list_type != PT_IS_SUBQUERY)
    {
      /* Search and check sub-inserts in value list */
      (void) parser_walk_tree (parser, node->info.insert.value_clauses, pt_check_sub_insert, NULL, NULL, NULL);
    }
      if (pt_has_error (parser))
    {
      break;
    }

      if (top_node && top_node->node_type != PT_INSERT && top_node->node_type != PT_SCOPE)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INS_EXPR_IN_INSERT);
    }
      break;

    case PT_EVALUATE:
      if (node)
    {
      node = pt_semantic_type (parser, node, info);
    }
      break;

    case PT_METHOD_CALL:
      if (node->info.method_call.call_or_expr == PT_IS_MTHD_EXPR)
    {
      pt_check_method (parser, node);
    }
      else if (node->info.method_call.call_or_expr == PT_IS_CALL_STMT)
    {
      if (node->info.method_call.method_type == PT_SP_PROCEDURE && node->info.method_call.to_return_var != NULL)
        {
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SP_CALL_WITH_INTO_CLAUSE);
          break;
        }

      /* Expressions in method calls from a CALL statement need to be typed explicitly since they are not wrapped
       * in a query and are not explicitly type-checked via pt_check_method().  This is due to a bad decision which
       * allowed users to refrain from fully typing methods before the advent of methods in queries. */
      node->info.method_call.arg_list = pt_semantic_type (parser, node->info.method_call.arg_list, info);
      node->info.method_call.on_call_target =
        pt_semantic_type (parser, node->info.method_call.on_call_target, info);
    }
      break;

    case PT_FUNCTION:
      func_info_p = &node->info.function;

      if (func_info_p->function_type == PT_GENERIC && (pt_length_of_list (func_info_p->arg_list) > NUM_F_GENERIC_ARGS))
    {
      PT_ERRORmf2 (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_GEN_FUNC_TOO_MANY_ARGS,
               func_info_p->generic_name, NUM_F_GENERIC_ARGS);
    }

      /* check order by for aggregate function */
      if (func_info_p->function_type == PT_GROUP_CONCAT)
    {
      if (pt_check_group_concat_order_by (parser, node) != NO_ERROR)
        {
          break;
        }
    }
      else if (func_info_p->function_type == PT_CUME_DIST || func_info_p->function_type == PT_PERCENT_RANK)
    {
      /* for CUME_DIST and PERCENT_RANK aggregate function */
      if (pt_check_cume_dist_percent_rank_order_by (parser, node) != NO_ERROR)
        {
          break;
        }
    }

      break;

    case PT_UNION:
    case PT_INTERSECTION:
    case PT_DIFFERENCE:
      if (top_node->flag.cannot_prepare == 1)
    {
      node->flag.cannot_prepare = 1;
    }

      /* semantic check {union|intersection|difference} operands */
      if (pt_has_error (parser))
    {
      break;
    }

      fold_as = pt_check_union_is_foldable (parser, node);
      if (fold_as != STATEMENT_SET_FOLD_NOTHING)
    {
      node = pt_fold_union (parser, node, fold_as);

      /* don't need do the following steps */
      break;
    }

      pt_check_into_clause (parser, node);

      /* check the orderby clause if present (all 3 nodes have SAME structure) */
      if (pt_check_order_by (parser, node) != NO_ERROR)
    {
      break;        /* error */
    }

      node = pt_semantic_type (parser, node, info);
      if (node == NULL)
    {
      break;
    }

      /* only root UNION nodes */
      if (node->info.query.q.union_.is_leaf_node == false)
    {
      PT_NODE *attrs = NULL;
      int cnt, k;
      SEMAN_COMPATIBLE_INFO *cinfo = NULL;
      PT_UNION_COMPATIBLE status;

      /* do collation inference necessary */
      attrs = pt_get_select_list (parser, node->info.query.q.union_.arg1);
      cnt = pt_length_of_select_list (attrs, EXCLUDE_HIDDEN_COLUMNS);

      cinfo = (SEMAN_COMPATIBLE_INFO *) malloc (cnt * sizeof (SEMAN_COMPATIBLE_INFO));
      if (cinfo == NULL)
        {
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          break;
        }

      for (k = 0; k < cnt; ++k)
        {
          cinfo[k].idx = -1;
          cinfo[k].type_enum = PT_TYPE_NONE;
          cinfo[k].prec = DB_DEFAULT_PRECISION;
          cinfo[k].scale = DB_DEFAULT_SCALE;
          cinfo[k].coll_infer.coll_id = LANG_SYS_COLLATION;
          cinfo[k].coll_infer.codeset = LANG_SYS_CODESET;
          cinfo[k].coll_infer.coerc_level = PT_COLLATION_NOT_COERC;
          cinfo[k].coll_infer.can_force_cs = false;
          cinfo[k].ref_att = NULL;
        }

      status = pt_get_select_list_coll_compat (parser, node, cinfo, cnt);
      if (status == PT_UNION_INCOMP)
        {
          (void) pt_apply_union_select_list_collation (parser, node, cinfo, cnt);
          free (cinfo);
          break;
        }

      free (cinfo);
    }
      break;

    case PT_SELECT:
      if (top_node->flag.cannot_prepare == 1)
    {
      node->flag.cannot_prepare = 1;
    }

      if (node->info.query.flag.single_tuple == 1)
    {
      if (pt_length_of_select_list (node->info.query.q.select.list, EXCLUDE_HIDDEN_COLUMNS) != 1)
        {
          /* illegal multi-column subquery */
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_SINGLE_COL);
        }
    }

      pt_check_into_clause (parser, node);

      if (node->info.query.q.select.with_increment)
    {
      PT_NODE *select_list = node->info.query.q.select.list;
      PT_NODE *hidden_list = node->info.query.q.select.with_increment;

      (void) parser_append_node (hidden_list, select_list);
      node->info.query.q.select.with_increment = NULL;

      /* Click count functions (e.g., INCR, DECR) can be used only in a top-level SELECT statement.
       * Setting the is_click_counter flag on both top_node and node may seem redundant,
       * but it is necessary in UPDATE statements where top_node and node are not the same. */
      top_node->flag.is_click_counter = 1;
      node->flag.is_click_counter = 1;
    }

      /* check the group by */
      if (pt_check_group_by (parser, node) != NO_ERROR)
    {
      break;        /* error */
    }

      if (pt_has_analytic (parser, node))
    {
      (void) parser_walk_tree (parser, node->info.query.q.select.list, pt_check_analytic_function, (void *) node,
                   NULL, NULL);
    }

      /* check the order by */
      if (pt_check_order_by (parser, node) != NO_ERROR)
    {
      break;        /* error */
    }

      if (node->info.query.q.select.connect_by)
    {
      bool has_level_greater, has_level_lesser;
      int disallow_ops[] = {
        2,          /* number of operators */
        PT_CONNECT_BY_ISCYCLE,
        PT_CONNECT_BY_ISLEAF,
      };
      (void) parser_walk_tree (parser, node->info.query.q.select.connect_by, pt_expr_disallow_op_pre, disallow_ops,
                   NULL, NULL);

      /* check if the LEVEL of connect by is limited */
      pt_check_level_expr (parser, node->info.query.q.select.connect_by, &has_level_greater, &has_level_lesser);
      if (has_level_lesser)
        {
          /* override checking cycles to be ignored */
          if (node->info.query.q.select.check_cycles == CONNECT_BY_CYCLES_NONE)
        {
          node->info.query.q.select.check_cycles = CONNECT_BY_CYCLES_NONE_IGNORE;
        }
          else
        {
          node->info.query.q.select.check_cycles = CONNECT_BY_CYCLES_IGNORE;
        }
        }
    }

      entity = node->info.query.q.select.from;
      if (entity != NULL && entity->info.spec.derived_table_type == PT_IS_SHOWSTMT
      && (derived_table = entity->info.spec.derived_table) != NULL && derived_table->node_type == PT_SHOWSTMT)
    {
      SHOWSTMT_TYPE show_type;
      SHOW_SEMANTIC_CHECK_FUNC check_func = NULL;

      show_type = derived_table->info.showstmt.show_type;
      check_func = showstmt_get_metadata (show_type)->semantic_check_func;
      if (check_func != NULL)
        {
          node = (*check_func) (parser, node);
        }
    }

      /* check for session variable assignments */
      {
    int arg[2];

    arg[0] = PT_DEFINE_VARIABLE;    /* type */
    arg[1] = 0;     /* found */

    (void) parser_walk_tree (parser, node->info.query.q.select.list, pt_find_op_type_pre, arg, NULL, NULL);

    if (arg[1])     /* an assignment was found */
      {
        PT_SELECT_INFO_SET_FLAG (node, PT_SELECT_INFO_DISABLE_LOOSE_SCAN);
      }
      }

      node = pt_semantic_type (parser, node, info);
      break;

    case PT_DO:
      node = pt_semantic_type (parser, node, info);
      break;

    case PT_SET_XACTION:
      /* Check for multiple isolation settings and multiple timeout settings */
      (void) pt_check_xaction_list (parser, node->info.set_xaction.xaction_modes);

      /* Check for mismatch of schema and instance isolation levels */
      (void) parser_walk_tree (parser, node->info.set_xaction.xaction_modes, pt_check_isolation_lvl, NULL, NULL, NULL);
      break;

    case PT_UPDATE:
      if (top_node->flag.cannot_prepare == 1)
    {
      node->flag.cannot_prepare = 1;
    }

      if (pt_has_aggregate (parser, node))
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UPDATE_NO_AGGREGATE);
    }

      pt_check_assignments (parser, node);
      if (pt_has_error (parser))
    {
      return NULL;
    }

      pt_no_double_updates (parser, node);

      /* cannot update derived tables */
      pt_init_assignments_helper (parser, &ea, node->info.update.assignment);
      while ((t_node = pt_get_next_assignment (&ea)) != NULL)
    {
      entity = pt_find_spec_in_statement (parser, node, t_node);

      if (entity == NULL)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_RESOLUTION_FAILED,
              t_node->info.name.original);
          break;
        }

      if (entity->info.spec.derived_table != NULL || PT_SPEC_IS_CTE (entity))
        {
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UPDATE_DERIVED_TABLE);
          break;
        }

      /* Update of views hierarchies not allowed */
      if (entity->info.spec.flat_entity_list    /* for local query only, but not for remote */
          && db_is_vclass (entity->info.spec.flat_entity_list->info.name.db_object) > 0
          && entity->info.spec.only_all == PT_ALL)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UPDATE_SUBVCLASS_NOT_ALLOWED,
              t_node->info.name.original);
          break;
        }
    }

      /* Replace left to right attribute references in assignments before doing semantic check. The type checking phase
       * might have to perform some coercions on the replaced names. */
      node = pt_replace_names_in_update_values (parser, node);

      node = pt_semantic_type (parser, node, info);

      if (node != NULL && node->info.update.order_by != NULL)
    {
      PT_NODE *order;
      for (order = node->info.update.order_by; order != NULL; order = order->next)
        {
          PT_NODE *r = order->info.sort_spec.expr;
          if (r != NULL && r->node_type == PT_VALUE)
        {
          PT_ERRORmf (parser, r, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NO_ORDERBY_ALLOWED,
                  pt_short_print (parser, r));
          break;
        }
        }
    }
      break;

    case PT_SET_SESSION_VARIABLES:
      node = pt_semantic_type (parser, node, info);
      break;

    case PT_EXPR:
      if (node->info.expr.op == PT_CAST)
    {
      node = pt_semantic_type (parser, node, info);
      if (node == NULL)
        {
          break;
        }

      if (node->node_type == PT_EXPR && node->info.expr.op == PT_CAST)
        {
          (void) pt_check_cast_op (parser, node);
        }
    }

      /* check instnum compatibility */
      if (pt_is_instnum (node) && PT_EXPR_INFO_IS_FLAGED (node, PT_EXPR_INFO_INSTNUM_NC))
    {
      PT_ERRORmf2 (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INSTNUM_COMPATIBILITY_ERR,
               "INST_NUM() or ROWNUM", "INST_NUM() or ROWNUM");
    }

      /* check default function */
      if (node->info.expr.op == PT_DEFAULTF)
    {
      pt_check_defaultf (parser, node);
    }

      break;

    case PT_SPEC:
      {
    PT_NODE *derived_table, *a, *b, *select_list;
    int attr_cnt, col_cnt, i, j;

    /* check ambiguity in as_attr_list of derived-query */
    for (a = node->info.spec.as_attr_list; a && !pt_has_error (parser); a = a->next)
      {
        for (b = a->next; b && !pt_has_error (parser); b = b->next)
          {
        if (a->node_type == PT_NAME && b->node_type == PT_NAME
            && !pt_str_compare (a->info.name.original, b->info.name.original, CASE_INSENSITIVE))
          {
            PT_ERRORmf (parser, b, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_AMBIGUOUS_REF_TO,
                b->info.name.original);
          }
          }
      }

    /* check hidden column of subquery-derived table */
    if (!pt_has_error (parser) && node->info.spec.derived_table_type == PT_IS_SUBQUERY
        && (derived_table = node->info.spec.derived_table) && derived_table->node_type == PT_SELECT
        && derived_table->info.query.order_by && (select_list = pt_get_select_list (parser, derived_table)))
      {
        attr_cnt = pt_length_of_list (node->info.spec.as_attr_list);
        col_cnt = pt_length_of_select_list (select_list, INCLUDE_HIDDEN_COLUMNS);
        if (col_cnt - attr_cnt > 0)
          {
        /* make hidden column attrs */
        for (i = attr_cnt, j = attr_cnt; i < col_cnt; i++)
          {
            t_node = pt_name (parser, mq_generate_name (parser, "ha", &j));
            node->info.spec.as_attr_list = parser_append_node (t_node, node->info.spec.as_attr_list);
          }
          }
      }
      }
      break;

    case PT_NAME:
      if (PT_IS_OID_NAME (node) && !PT_NAME_INFO_IS_FLAGED (node, PT_NAME_INFO_GENERATED_OID)
      && !PT_NAME_INFO_IS_FLAGED (node, PT_NAME_ALLOW_REUSABLE_OID))
    {
      PT_NODE *data_type = node->data_type;

      if (data_type != NULL && data_type->type_enum == PT_TYPE_OBJECT)
        {
          const char *name = data_type->info.data_type.entity->info.name.original;
          DB_OBJECT *class_obj = db_find_class (name);

          if (class_obj != NULL && sm_is_reuse_oid_class (class_obj))
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NON_REFERABLE_VIOLATION, name);
        }
        }
    }
      break;

    case PT_MERGE:
      if (pt_has_error (parser))
    {
      break;
    }

      if (top_node->flag.cannot_prepare == 1)
    {
      node->flag.cannot_prepare = 1;
    }

      if (pt_has_aggregate (parser, node))
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_WANT_NO_AGGREGATE);
      break;
    }

      pt_check_assignments (parser, node);
      if (pt_has_error (parser))
    {
      return NULL;
    }

      pt_no_double_updates (parser, node);

      /* check destination derived table */
      entity = node->info.merge.into;
      if (entity->info.spec.derived_table != NULL)
    {
      PT_ERRORm (parser, entity, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_MERGE_DERIVED_TABLE);
      break;
    }

      /* check update spec */
      pt_init_assignments_helper (parser, &ea, node->info.merge.update.assignment);
      while ((t_node = pt_get_next_assignment (&ea)) != NULL)
    {
      entity = pt_find_spec_in_statement (parser, node, t_node);

      if (entity == NULL)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_RESOLUTION_FAILED,
              t_node->info.name.original);
          break;
        }
      /* update assign spec should be merge target */
      if (entity->info.spec.id != node->info.merge.into->info.spec.id)
        {
          PT_ERRORm (parser, t_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_MERGE_CANT_AFFECT_SOURCE_TABLE);
          break;
        }
    }
      if (pt_has_error (parser))
    {
      break;
    }

      if (node->info.merge.insert.value_clauses)
    {
      if (node->info.merge.insert.value_clauses->next)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DO_INSERT_TOO_MANY, 0);
          if (!pt_has_error (parser))
        {
          PT_ERRORc (parser, node, db_error_string (3));
        }
          break;
        }
      /* check insert attributes list */
      for (entity = node->info.merge.insert.attr_list; entity; entity = entity->next)
        {
          if (entity->info.name.spec_id != node->info.merge.into->info.spec.id)
        {
          PT_ERRORm (parser, entity, MSGCAT_SET_PARSER_SEMANTIC,
                 MSGCAT_SEMANTIC_MERGE_CANT_AFFECT_SOURCE_TABLE);
          break;
        }
        }
      if (pt_has_error (parser))
        {
          break;
        }
    }

      node = pt_semantic_type (parser, node, info);

      /* try to coerce insert_values into types indicated by insert_attributes */
      if (node && node->info.merge.into && node->info.merge.into->info.spec.remote_server_name == NULL)
    {
      pt_coerce_insert_values (parser, node);
    }
      break;

    case PT_JSON_TABLE:
      if (pt_has_error (parser))
    {
      break;
    }

      if (!pt_is_json_doc_type (node->info.json_table_info.expr->type_enum))
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_WANT_TYPE,
              pt_show_type_enum (PT_TYPE_JSON));
    }

      if (pt_check_json_table_node (parser, node->info.json_table_info.tree))
    {
      ASSERT_ERROR ();
      PT_ERRORc (parser, node, er_msg ());
    }

      break;

    case PT_DBLINK_TABLE:
    case PT_DBLINK_TABLE_DML:
      if (pt_has_error (parser))
    {
      break;
    }

      break;

    default:            /* other node types */
      break;
    }

  /* Select Aliasing semantic checking of select aliasing, check if it is zero-length string, i.e. "" Only appropriate
   * PT_NODE to be check will have 'alias' field as not NULL pointer because the initialized value of 'alias' is NULL
   * pointer. So it is safe to do semantic checking of aliasing out of the scope of above 'switch' statement and
   * without considering type of the PT_NODE. */
  if (node && node->alias_print && *(node->alias_print) == '\0')
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_DEFINED, "\"\"");
    }

  /* restore list link, if any */
  if (node)
    {
      node->next = next;
    }

  if (pt_has_error (parser))
    {
      if (node)
    {
      pt_register_orphan (parser, node);
    }
      else
    {
      pt_register_orphan (parser, orig);
    }
      return NULL;
    }
  else
    {
      return node;
    }
}

/*
 * pt_gen_isnull_preds () - Construct the IS NULL disjuncts for the expanded
 *                          list of path segments
 *   return:
 *   parser(in):
 *   pred(in):
 *   chain(in):
 */
static PT_NODE *
pt_gen_isnull_preds (PARSER_CONTEXT * parser, PT_NODE * pred, PT_CHAIN_INFO * chain)
{
  PT_NODE *disj, *arg1, *next_spec, *conj, *new_path, *new_expr;
  PT_NODE *new_pred = NULL;
  UINTPTR next_spec_id;
  int i;

  /* The algorithm here is that we will incrementally build each new "IS NULL" disjunct.  Each successive disjunct will
   * contain the previous path expression extended with the next path segment.  We will use arg1 to build each
   * successive path expression.  new_pred will collect the new disjuncts as we build them. */

  arg1 = NULL;
  for (i = 0; i < chain->chain_length - 1; i++)
    {
      /* Remember that the chain was constructed from the end of the path expression to the beginning.  Thus, in path
       * expr a.b.c.d is null, segment d is in chain[0], c is in chain[1], b is in chain[2], and a is in chain[3].
       * Also, by convention, the path conjuncts implied by a path expression segment are hung off the path entity that
       * is generated by the path expression segment.  In our case, this is the next spec in the chain. */

      next_spec = chain->chain_ptr[chain->chain_length - i - 2];
      next_spec_id = next_spec->info.spec.id;
      conj = next_spec->info.spec.path_conjuncts;

      /* check for structural errors */
      if ((conj->node_type != PT_EXPR) || (!conj->info.expr.arg1) || (!conj->info.expr.arg2)
      || (conj->info.expr.arg1->node_type != PT_NAME) || (conj->info.expr.arg2->node_type != PT_NAME)
      || (conj->info.expr.arg2->info.name.spec_id != next_spec_id))
    {
      goto error;
    }

      if (arg1 == NULL)
    {
      /* This is the first segment in the path expression.  In this case we want to use the exposed name of the
       * spec found in the last chain slot.  (i should be 0 here) */
      arg1 = parser_copy_tree (parser, conj->info.expr.arg1);
      if (arg1 == NULL)
        {
          goto out_of_mem;
        }
    }
      else
    {
      PT_NODE *arg2;

      /* we are building a new segment on the previous path expr */
      if (((new_path = parser_new_node (parser, PT_DOT_)) == NULL)
          || ((arg2 = parser_copy_tree (parser, conj->info.expr.arg1)) == NULL))
        {
          goto out_of_mem;
        }

      /* We need to null the resolved field of arg2 according to path expression conventions.  This is necessary
       * since we copied part of the conjunct which is fully resolved. */
      arg2->info.name.resolved = NULL;

      /* attach both arguments to the new path segment */
      new_path->info.expr.arg1 = parser_copy_tree (parser, arg1);
      if (new_path->info.expr.arg1 == NULL)
        {
          goto out_of_mem;
        }

      new_path->info.expr.arg2 = arg2;

      /* attach the data type */
      new_path->line_number = pred->line_number;
      new_path->column_number = pred->column_number;
      new_path->type_enum = arg2->type_enum;
      if (arg2->data_type && ((new_path->data_type = parser_copy_tree_list (parser, arg2->data_type)) == NULL))
        {
          goto out_of_mem;
        }

      /* Maintain the loop invariant that arg1 always is the path expression that we build on. */
      arg1 = new_path;
    }

      /* Link it in with a disjunct. */
      disj = parser_new_node (parser, PT_EXPR);
      if (disj == NULL)
    {
      goto out_of_mem;
    }

      disj->line_number = pred->line_number;
      disj->column_number = pred->column_number;
      disj->type_enum = PT_TYPE_LOGICAL;
      disj->info.expr.op = PT_IS_NULL;
      disj->info.expr.arg1 = arg1;

      if (new_pred == NULL)
    {
      /* Maintain the loop invariant that new_pred contains the predicate built so far. */
      new_pred = disj;
    }
      else
    {
      new_expr = parser_new_node (parser, PT_EXPR);
      if (new_expr == NULL)
        {
          goto out_of_mem;
        }

      new_expr->line_number = pred->line_number;
      new_expr->column_number = pred->column_number;
      new_expr->type_enum = PT_TYPE_LOGICAL;
      new_expr->info.expr.op = PT_OR;
      new_expr->info.expr.arg1 = new_pred;
      new_expr->info.expr.arg2 = disj;

      /* Maintain the loop invariant that new_pred contains the predicate built so far. */
      new_pred = new_expr;
    }
    }

  new_expr = parser_new_node (parser, PT_EXPR);
  if (new_expr == NULL)
    {
      goto out_of_mem;
    }

  new_expr->line_number = pred->line_number;
  new_expr->column_number = pred->column_number;
  new_expr->type_enum = PT_TYPE_LOGICAL;
  new_expr->info.expr.op = PT_OR;
  new_expr->info.expr.arg1 = pred;
  new_expr->info.expr.arg2 = new_pred;
  new_expr->info.expr.paren_type = 1;

  return new_expr;

out_of_mem:
  PT_ERRORm (parser, pred, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
  return NULL;

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

/*
 * pt_path_chain () - Construct the list of path entities that are used
 *                    in a path expression
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
pt_path_chain (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_CHAIN_INFO *chain = (PT_CHAIN_INFO *) arg;
  PT_NODE *tmp;

  switch (node->node_type)
    {
    case PT_SPEC:
      if (node->info.spec.id == (UINTPTR) chain->spec_id)
    {
      /* This is the spec to which the final path segment resolves. Start gathering the spec chain. */
      chain->chain_ptr[0] = node;
      chain->chain_length = 1;
    }
      else if (chain->chain_length > 0)
    {
      /* This indicates that we are currently walking up the chain. Need to check if this spec is the parent of the
       * last spec. */
      for (tmp = node->info.spec.path_entities; tmp != NULL; tmp = tmp->next)
        {
          if (tmp == chain->chain_ptr[chain->chain_length - 1])
        {
          /* This is the parent, add it to the list. First check if we have space. */
          if (chain->chain_length == chain->chain_size)
            {
              /* Need to expand, just double the size. */

              chain->chain_size *= 2;
              if (chain->chain_ptr == chain->chain)
            {
              /* This will be the first time we need to alloc. */
              chain->chain_ptr = (PT_NODE **) malloc (chain->chain_size * sizeof (PT_NODE *));
              if (chain->chain_ptr == NULL)
                {
                  goto out_of_mem;
                }
              memcpy (chain->chain_ptr, &chain->chain, (chain->chain_length * sizeof (PT_NODE *)));
            }
              else
            {
              PT_NODE **tmp;

              tmp = (PT_NODE **) realloc (chain->chain_ptr, (chain->chain_size * sizeof (PT_NODE *)));
              if (tmp == NULL)
                {
                  goto out_of_mem;
                }
              chain->chain_ptr = tmp;
            }
            }

          /* Add in the parent. */
          chain->chain_ptr[chain->chain_length] = node;
          chain->chain_length++;
        }
        }
    }
      break;

    case PT_SELECT:
    case PT_DELETE:
    case PT_UPDATE:
    case PT_MERGE:
      if (chain->chain_length > 0)
    {
      /* We are about to leave the scope where the chain was found, we can safely stop the walk since we must have
       * found the whole chain. */
      *continue_walk = PT_STOP_WALK;
    }
      break;

    default:
      break;
    }

  return node;

out_of_mem:
  PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);

  *continue_walk = PT_STOP_WALK;
  return node;
}

/*
 * pt_expand_isnull_preds_helper () - expand path_expr "IS NULL" predicates to
 *                                    include any path segment being NULL
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
pt_expand_isnull_preds_helper (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE *statement = (PT_NODE *) arg;
  PT_CHAIN_INFO chain_info;

  if (node->node_type == PT_EXPR && node->info.expr.op == PT_IS_NULL && node->info.expr.arg1->node_type == PT_DOT_)
    {
      chain_info.chain_ptr = chain_info.chain;
      chain_info.chain_size = PT_CHAIN_LENGTH;
      chain_info.chain_length = 0;
      chain_info.spec_id = node->info.expr.arg1->info.dot.arg2->info.name.spec_id;

      (void) parser_walk_tree (parser, statement, NULL, NULL, pt_path_chain, &chain_info);

      /* now that we have the chain, we need to construct the new "IS NULL" disjuncts. */
      if (!pt_has_error (parser) && chain_info.chain_length > 1)
    {
      node = pt_gen_isnull_preds (parser, node, &chain_info);
    }

      /* Free any allocated memory for the spec chain. */
      if (chain_info.chain_ptr != chain_info.chain)
    {
      free_and_init (chain_info.chain_ptr);
    }
    }

  return node;
}

/*
 * pt_expand_isnull_preds () - expand path_expr "IS NULL" predicates to
 *                             include any path segment being NULL
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
pt_expand_isnull_preds (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE *statement = (PT_NODE *) arg;
  PT_NODE **pred = NULL;

  switch (node->node_type)
    {
    case PT_UPDATE:
      pred = &node->info.update.search_cond;
      break;

    case PT_DELETE:
      pred = &node->info.delete_.search_cond;
      break;

    case PT_SELECT:
      pred = &node->info.query.q.select.where;
      break;

    case PT_MERGE:
      pred = &node->info.merge.insert.search_cond;
      if (pred)
    {
      *pred = parser_walk_tree (parser, *pred, NULL, NULL, pt_expand_isnull_preds_helper, statement);
    }
      pred = &node->info.merge.update.search_cond;
      if (pred)
    {
      *pred = parser_walk_tree (parser, *pred, NULL, NULL, pt_expand_isnull_preds_helper, statement);
    }
      pred = &node->info.merge.update.del_search_cond;
      if (pred)
    {
      *pred = parser_walk_tree (parser, *pred, NULL, NULL, pt_expand_isnull_preds_helper, statement);
    }
      pred = &node->info.merge.search_cond;
      break;

    default:
      break;
    }

  if (pred)
    {
      *pred = parser_walk_tree (parser, *pred, NULL, NULL, pt_expand_isnull_preds_helper, statement);
    }

  return node;
}

/*
 * pt_check_and_replace_hostvar () -
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_check_and_replace_hostvar (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE *value;
  DB_VALUE *dbval;
  DB_TYPE type;
  int *check = (int *) arg;

  /* do not replace path expression */
  if (pt_is_dot_node (node))
    {
      if (pt_is_input_parameter (node->info.dot.arg1) || pt_is_input_parameter (node->info.dot.arg2))
    {
      *check = 1;       /* this statement cannot be prepared */
    }
      *continue_walk = PT_LIST_WALK;
      return node;
    }

  /* method check */
  if (pt_is_method_call (node))
    {
      *check = 1;       /* this statement cannot be prepared */
      return node;
    }

  /* replace input host var/parameter with its value if given */
  if ((pt_is_input_hostvar (node) && parser->host_var_count > node->info.host_var.index
       && parser->flag.set_host_var == 1) || pt_is_input_parameter (node))
    {
      type = pt_node_to_db_type (node);
      if (type == DB_TYPE_OBJECT || type == DB_TYPE_VOBJ || TP_IS_SET_TYPE (type))
    {
      if (pt_is_input_parameter (node))
        {
          *check = 1;   /* this statement cannot be prepared */
        }
      return node;
    }

      dbval = pt_value_to_db (parser, node);
      if (dbval && !pr_is_set_type (db_value_type (dbval)))
    {
      value = pt_dbval_to_value (parser, dbval);
      if (value)
        {
          if (PT_HAS_COLLATION (value->type_enum))
        {
          value->info.value.print_charset = true;
          value->info.value.print_collation = true;
          value->info.value.is_collate_allowed = true;
        }
          if (pt_is_input_hostvar (node))
        {
          /* save host_var index */
          value->info.value.host_var_index = node->info.host_var.index;
        }
          PT_NODE_MOVE_NUMBER_OUTERLINK (value, node);
          parser_free_tree (parser, node);
          node = value;
        }
    }
    }

  return node;
}

/*
 * pt_check_with_clause () -
 *   do semantic checks on "create entity" has "with clause"
 *   this function is called from pt_check_create_entity only
 *   so, not needed to check NULL for node
 *
 *   return:  NULL if no errors, attribute of CTE otherwise
 *   node(in): a "with" statement that needs to be checked.
 */

static PT_NODE *
pt_check_with_clause (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *cte = node->info.query.with->info.with_clause.cte_definition_list;

  if (cte)
    {
      PT_NODE *cte_part;
      PT_NODE *cte_attr;

      if (cte->info.cte.as_attr_list == NULL)
    {
      cte_part = cte->info.cte.non_recursive_part;
      if (cte_part)
        {
          for (cte_attr = pt_get_select_list (parser, cte_part); cte_attr != NULL; cte_attr = cte_attr->next)
        {
          if (cte_attr->alias_print == NULL && cte_attr->node_type != PT_NAME && cte_attr->node_type != PT_DOT_)
            {
              return cte_attr;
            }
        }
        }

      cte_part = cte->info.cte.recursive_part;
      if (cte_part)
        {
          for (cte_attr = pt_get_select_list (parser, cte_part); cte_attr != NULL; cte_attr = cte_attr->next)
        {
          if (cte_attr->alias_print == NULL && cte_attr->node_type != PT_NAME && cte_attr->node_type != PT_DOT_)
            {
              return cte_attr;
            }
        }
        }
    }
    }

  return NULL;
}

/*
 * pt_check_with_info () -  do name resolution & semantic checks on this tree
 *   return:  statement if no errors, NULL otherwise
 *   parser(in): the parser context
 *   node(in): a parsed sql statement that needs to be checked.
 *   info(in): NULL or info->attrdefs is a vclass' attribute defs list
 */

static PT_NODE *
pt_check_with_info (PARSER_CONTEXT * parser, PT_NODE * node, SEMANTIC_CHK_INFO * info)
{
  PT_NODE *next;
  SEMANTIC_CHK_INFO sc_info = { NULL, NULL, 0, 0, 0, false, false };
  SEMANTIC_CHK_INFO *sc_info_ptr = info;
  bool save_donot_fold = false;

  assert (parser != NULL);

  if (!node)
    {
      return NULL;
    }

  /* If it is an internally created statement, set its host variable info again to search host variables at parent
   * parser */
  SET_HOST_VARIABLES_IF_INTERNAL_STATEMENT (parser);

  if (sc_info_ptr == NULL)
    {
      sc_info_ptr = &sc_info;
    }
  if (info)
    {
      save_donot_fold = sc_info_ptr->donot_fold;    /* save */
    }

  sc_info_ptr->top_node = node;
  sc_info_ptr->donot_fold = false;
  next = node->next;
  node->next = NULL;

  if (pt_is_ddl_statement (node))
    {
      tdes_set_query_start_info (node->sql_user_text);
    }

  switch (node->node_type)
    {
    case PT_UPDATE:
      /*
       * If it is an update object, get the object to update, and create an
       * entity so that pt_resolve_names will work.
       * THIS NEEDS TO BE MOVED INTO RESOLVE NAMES.
       */
      if (node->info.update.object_parameter != NULL)
    {
      pt_resolve_object (parser, node);
    }
      [[fallthrough]];

    case PT_HOST_VAR:
    case PT_EXPR:
    case PT_NAME:
    case PT_VALUE:
    case PT_FUNCTION:

    case PT_DELETE:
    case PT_INSERT:
    case PT_METHOD_CALL:
    case PT_UNION:
    case PT_INTERSECTION:
    case PT_DIFFERENCE:
    case PT_SELECT:
    case PT_EVALUATE:
    case PT_SET_XACTION:
    case PT_SCOPE:
    case PT_DO:
    case PT_SET_SESSION_VARIABLES:
    case PT_MERGE:
#if 0               /* to disable TEXT */
      /* we postpone TEXT resolution of a insert statement at '*' resolution if (node->node_type == PT_INSERT) {
       * pt_resolve_insert_external(parser, node); } else */
      if (node->node_type == PT_DELETE)
    {
      pt_resolve_delete_external (parser, node);
    }
      else if (node->node_type == PT_UPDATE)
    {
      pt_resolve_update_external (parser, node);
    }
#endif /* 0 */

      if (!pt_has_error (parser))
    {
      if ((node->node_type == PT_INSERT && node->info.insert.spec->info.spec.remote_server_name)
          || (node->node_type == PT_DELETE && node->info.delete_.spec->info.spec.remote_server_name)
          || (node->node_type == PT_UPDATE && node->info.update.spec->info.spec.remote_server_name)
          || (node->node_type == PT_MERGE && node->info.merge.into->info.spec.remote_server_name))
        {
          break;
        }
    }

      sc_info_ptr->system_class = false;
      node = pt_resolve_names (parser, node, sc_info_ptr);

      if (!pt_has_error (parser))
    {
      node = pt_check_where (parser, node);
    }

      if (!pt_has_error (parser))
    {
      if (sc_info_ptr->system_class && PT_IS_QUERY (node))
        {
          /* do not cache the result if a system class is involved in the query */
          node->info.query.flag.reexecute = 1;
          node->info.query.flag.do_cache = 0;
          node->info.query.flag.do_not_cache = 1;
          node->info.query.flag.has_system_class = 1;
        }

      if (node->node_type == PT_UPDATE || node->node_type == PT_DELETE || node->node_type == PT_INSERT
          || node->node_type == PT_UNION || node->node_type == PT_INTERSECTION || node->node_type == PT_DIFFERENCE
          || node->node_type == PT_SELECT || node->node_type == PT_DO || node->node_type == PT_SET_SESSION_VARIABLES
          || node->node_type == PT_MERGE)
        {
          /* may have WHERE clause */
          int check = 0;

          node = parser_walk_tree (parser, node, pt_check_and_replace_hostvar, &check, pt_continue_walk, NULL);
          if (check)
        {
          node->flag.cannot_prepare = 1;
        }

          /* because the statement has some object type PT_PARAMETER, it cannot be prepared */
          if (parent_parser != NULL)
        {
          node->flag.cannot_prepare = 1;
        }
        }

      node = parser_walk_tree (parser, node, pt_mark_union_leaf_nodes, NULL, pt_continue_walk, NULL);

      if (!pt_has_error (parser))
        {
          /* remove unnecessary variable */
          node = parser_walk_tree (parser, node, NULL, NULL, pt_semantic_check_local, sc_info_ptr);

          if (!pt_has_error (parser))
        {
          /* This must be done before CNF since we are adding disjuncts to the "IS NULL" expression. */
          node = parser_walk_tree (parser, node, pt_expand_isnull_preds, node, NULL, NULL);
        }
        }
    }

      break;

    case PT_CREATE_INDEX:
    case PT_ALTER_INDEX:
    case PT_DROP_INDEX:
      if (parser->host_var_count)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_HOSTVAR_IN_DDL);
    }
      else
    {
      sc_info_ptr->system_class = false;
      node = pt_resolve_names (parser, node, sc_info_ptr);
      if (!pt_has_error (parser) && node->node_type == PT_CREATE_INDEX)
        {
          pt_check_create_index (parser, node);
        }

      if (!pt_has_error (parser) && (node->node_type == PT_DROP_INDEX || node->node_type == PT_ALTER_INDEX))
        {
          pt_check_function_index_expr (parser, node);
        }

      if (!pt_has_error (parser) && node->node_type == PT_ALTER_INDEX)
        {
          DB_OBJECT *db_obj = NULL;
          PT_NODE *name = NULL;

          if (node->info.index.indexed_class)
        {
          name = node->info.index.indexed_class->info.spec.entity_name;
          db_obj = db_find_class (name->info.name.original);

          if (db_obj == NULL)
            {
              PT_ERRORmf (parser, name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_A_CLASS,
                  name->info.name.original);
            }
        }

          if (!pt_has_error (parser))
        {
          pt_check_filter_index_expr (parser, node->info.index.column_names, node->info.index.where, db_obj);
        }
        }

      if (!pt_has_error (parser))
        {
          node = pt_semantic_type (parser, node, info);
        }

      if (node && !pt_has_error (parser))
        {
          if (node->info.index.where && pt_false_search_condition (parser, node->info.index.where))
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_FILTER_INDEX,
                  pt_short_print (parser, node->info.index.where));
        }
        }

      if (node && !pt_has_error (parser))
        {
          if (node->info.index.function_expr
          && !pt_is_function_index_expr (parser, node->info.index.function_expr->info.sort_spec.expr, true))
        {
          break;
        }
        }

      if (node && !pt_has_error (parser))
        {
          node = parser_walk_tree (parser, node, NULL, NULL, pt_semantic_check_local, sc_info_ptr);

          if (!pt_has_error (parser))
        {
          /* This must be done before CNF since we are adding disjuncts to the "IS NULL" expression. */
          node = parser_walk_tree (parser, node, pt_expand_isnull_preds, node, NULL, NULL);
        }
        }
    }
      break;

    case PT_SAVEPOINT:
      if ((node->info.savepoint.save_name) && (node->info.savepoint.save_name->info.name.meta_class == PT_PARAMETER))
    {
      node = pt_resolve_names (parser, node, sc_info_ptr);
    }
      break;

    case PT_ROLLBACK_WORK:
      if ((node->info.rollback_work.save_name)
      && (node->info.rollback_work.save_name->info.name.meta_class == PT_PARAMETER))
    {
      node = pt_resolve_names (parser, node, sc_info_ptr);
    }
      break;

    case PT_AUTH_CMD:
      break;            /* see GRANT/REVOKE */

    case PT_DROP:
      pt_check_drop (parser, node);
      break;

    case PT_VACUUM:
      pt_check_vacuum (parser, node);
      break;

    case PT_GRANT:
    case PT_REVOKE:
      pt_check_grant_revoke (parser, node);
      break;

    case PT_TRUNCATE:
      pt_check_truncate (parser, node);
      break;

    case PT_KILL_STMT:
      pt_check_kill (parser, node);
      break;

    case PT_ALTER_SERIAL:
      pt_check_alter_serial (parser, node);
      break;
    case PT_ALTER_TRIGGER:
    case PT_ALTER_USER:
    case PT_CREATE_SERIAL:
    case PT_CREATE_TRIGGER:
    case PT_DROP_SERIAL:
    case PT_DROP_TRIGGER:
    case PT_DROP_USER:
    case PT_RENAME:
    case PT_RENAME_TRIGGER:
      break;

    case PT_UPDATE_STATS:
      pt_check_update_stats (parser, node);
      break;

    case PT_CREATE_SERVER:
    case PT_DROP_SERVER:
    case PT_RENAME_SERVER:
    case PT_ALTER_SERVER:
      break;

    case PT_ALTER:
      pt_check_alter (parser, node);

      if (node->info.alter.code == PT_ADD_ATTR_MTHD || node->info.alter.code == PT_ADD_INDEX_CLAUSE)
    {
      if (parser->host_var_count)
        {
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_HOSTVAR_IN_DDL);
          break;
        }
    }

      if (node->info.alter.code == PT_ADD_INDEX_CLAUSE)
    {
      /* apply typechecking on ALTER TABLE ADD INDEX statements, to check the expression in the WHERE clause of
       * a partial index */
      PT_NODE *p = node->info.alter.create_index;
      assert (p != NULL);

      while (p)
        {
          sc_info_ptr->system_class = false;
          p = pt_resolve_names (parser, p, sc_info_ptr);
          if (p && !pt_has_error (parser))
        {
          pt_check_create_index (parser, p);
        }

          if (!pt_has_error (parser))
        {
          p = pt_semantic_type (parser, p, info);
        }

          if (p && !pt_has_error (parser))
        {
          p = parser_walk_tree (parser, p, NULL, NULL, pt_semantic_check_local, sc_info_ptr);

          if (p && !pt_has_error (parser))
            {
              /* This must be done before CNF since we are adding disjuncts to the "IS NULL" expression. */
              p = parser_walk_tree (parser, p, pt_expand_isnull_preds, p, NULL, NULL);
            }
        }

          if (p != NULL && !pt_has_error (parser) && p->info.index.function_expr != NULL
          && !pt_is_function_index_expr (parser, p->info.index.function_expr->info.sort_spec.expr, true))
        {
          break;
        }
          if (p && !pt_has_error (parser))
        {
          p = p->next;
        }
          else
        {
          break;
        }
        }
    }
      break;

    case PT_CREATE_ENTITY:
      pt_check_create_entity (parser, node);
      break;

    case PT_CREATE_USER:
      pt_check_create_user (parser, node);
      break;

    case PT_ALTER_SYNONYM:
      pt_check_alter_synonym (parser, node);
      break;

    case PT_CREATE_SYNONYM:
      pt_check_create_synonym (parser, node);
      break;

    case PT_DROP_SYNONYM:
      pt_check_drop_synonym (parser, node);
      break;

    case PT_RENAME_SYNONYM:
      pt_check_rename_synonym (parser, node);
      break;

    case PT_CREATE_STORED_PROCEDURE:
      pt_check_create_stored_procedure (parser, node);
      break;

    default:
      break;
    }

  /* restore list link, if any */
  if (node)
    {
      node->next = next;
    }

  if (info)
    {
      sc_info_ptr->donot_fold = save_donot_fold;    /* restore */
    }

  RESET_HOST_VARIABLES_IF_INTERNAL_STATEMENT (parser);

  if (er_errid () == ER_LK_UNILATERALLY_ABORTED)
    {
      PT_ERRORc (parser, node, db_error_string (3));
    }

  if (pt_has_error (parser))
    {
      tdes_reset_query_start_info (node);
      pt_register_orphan (parser, node);
      return NULL;
    }
  else
    {
      return node;
    }
}

/*
 * pt_semantic_quick_check_node () - perform semantic validation on a
 *                   node that is not necessarily part of a
 *                   statement
 * return : modified node or NULL on error
 * parser (in)      : parser context
 * entity_name (in) : PT_NAME of the class containing attributes from node
 * node (in)        : node to check
 *
 *  Note: Callers of this function need both the spec and the node after
 *  the call. This is why we have to pass pointers to PT_NODE*
 */
PT_NODE *
pt_semantic_quick_check_node (PARSER_CONTEXT * parser, PT_NODE ** spec_p, PT_NODE ** node_p)
{
  SEMANTIC_CHK_INFO sc_info = { NULL, NULL, 0, 0, 0, false, false };
  int error = NO_ERROR;
  PT_NODE *node = NULL;

  /* resolve names */
  error = pt_quick_resolve_names (parser, spec_p, node_p, &sc_info);
  if (error != NO_ERROR || pt_has_error (parser))
    {
      return NULL;
    }

  node = *node_p;

  /* perform semantic check */
  node = pt_semantic_type (parser, node, &sc_info);
  if (node == NULL)
    {
      return NULL;
    }
  node_p = &node;
  return node;
}

/*
 * pt_semantic_check () -
 *   return: PT_NODE *(modified) if no errors, else NULL if errors
 *   parser(in):
 *   node(in): statement a parsed sql statement that needs to be checked
 */

PT_NODE *
pt_semantic_check (PARSER_CONTEXT * parser, PT_NODE * node)
{
  return pt_check_with_info (parser, node, NULL);
}

/*
 * pt_find_class () -
 *   return: DB_OBJECT * for the class whose name is in p,
 *           NULL if not a class name
 *   parser(in):
 *   p(in): a PT_NAME node
 *
 * Note :
 * Finds CLASS VCLASS VIEW only
 */
static DB_OBJECT *
pt_find_class (PARSER_CONTEXT * parser, PT_NODE * p, bool for_update)
{
  if (!p)
    return 0;

  if (p->node_type != PT_NAME)
    return 0;

  return db_find_class_with_purpose (p->info.name.original, for_update);
}


/*
 * pt_check_unique_attr () - check that there are no duplicate attr
 *                           in given list
 *   return: none
 *   parser(in): the parser context
 *   entity_name(in): class name or index name
 *   att(in): an attribute definition list
 *   att_type(in): an attribute definition type list
 */
static void
pt_check_unique_attr (PARSER_CONTEXT * parser, const char *entity_name, PT_NODE * att, PT_NODE_TYPE att_type)
{
  PT_NODE *p, *q, *p_nam, *q_nam;

  assert (parser != NULL);
  if (!att)
    {
      return;
    }

  for (p = att; p; p = p->next)
    {
      if (p->node_type != att_type)
    {
      continue;     /* give up */
    }

      p_nam = NULL;     /* init */
      if (att_type == PT_ATTR_DEF)
    {
      p_nam = p->info.attr_def.attr_name;
    }
      else if (att_type == PT_SORT_SPEC)
    {
      p_nam = p->info.sort_spec.expr;
    }

      if (p_nam == NULL || p_nam->node_type != PT_NAME)
    {
      continue;     /* give up */
    }

      for (q = p->next; q; q = q->next)
    {
      if (q->node_type != att_type)
        {
          continue;     /* give up */
        }

      q_nam = NULL;     /* init */
      if (att_type == PT_ATTR_DEF)
        {
          q_nam = q->info.attr_def.attr_name;
        }
      else if (att_type == PT_SORT_SPEC)
        {
          q_nam = q->info.sort_spec.expr;
        }

      if (q_nam == NULL || q_nam->node_type != PT_NAME)
        {
          continue;     /* give up */
        }

      /* a class attribute and a normal attribute can have identical names */
      if (att_type == PT_ATTR_DEF)
        {
          if (p->info.attr_def.attr_type != q->info.attr_def.attr_type)
        {
          continue; /* OK */
        }
        }

      if (!pt_str_compare (p_nam->info.name.original, q_nam->info.name.original, CASE_INSENSITIVE))
        {
          if (att_type == PT_ATTR_DEF)  /* is class entity */
        {
          PT_ERRORmf2 (parser, q_nam, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_ATTR_DUPLICATED,
                   q_nam->info.name.original, entity_name);
        }
          else      /* is index entity */
        {
          PT_ERRORmf (parser, q_nam, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INDEX_ATTR_DUPLICATED,
                  q_nam->info.name.original);
        }
        }
    }
    }
}

/*
 * pt_assignment_class_compatible () - Make sure that the rhs is a valid
 *                                     candidate for assignment into the lhs
 *   return: error_code
 *   parser(in): handle to context used to parse the insert statement
 *   lhs(in): the AST form of an attribute from the namelist part of an insert
 *   rhs(in): the AST form of an expression from the values part of an insert
 */
static int
pt_assignment_class_compatible (PARSER_CONTEXT * parser, PT_NODE * lhs, PT_NODE * rhs)
{
  assert (parser != NULL);
  assert (lhs != NULL);
  assert (lhs->node_type == PT_NAME);
  assert (lhs->type_enum != PT_TYPE_NONE);
  assert (rhs != NULL);

  if (lhs->node_type == PT_NAME)
    {
      if (lhs->type_enum == PT_TYPE_OBJECT)
    {
      if (!pt_class_assignable (parser, lhs->data_type, rhs))
        {
          /* incompatible object domains */
          PT_ERRORmf (parser, rhs, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INCOMP_TYPE_ON_ATTR,
              lhs->info.name.original);
          return ER_FAILED;
        }
    }
    }

  return NO_ERROR;
}

/*
 * pt_assignment_compatible () - Make sure that the rhs is a valid candidate
 *                           for assignment into the lhs
 *   return: the rhs node if compatible, NULL for errors
 *   parser(in): handle to context used to parse the insert statement
 *   lhs(in): the AST form of an attribute from the namelist part of an insert
 *   rhs(in): the AST form of an expression from the values part of an insert
 */
static PT_NODE *
pt_assignment_compatible (PARSER_CONTEXT * parser, PT_NODE * lhs, PT_NODE * rhs)
{
  assert (parser != NULL);
  assert (lhs != NULL);
  assert (lhs->node_type == PT_NAME);
  assert (rhs != NULL);

  if (lhs->type_enum == PT_TYPE_OBJECT)
    {
      if (rhs->node_type == PT_HOST_VAR && (rhs->type_enum == PT_TYPE_NONE || rhs->type_enum == PT_TYPE_MAYBE))
    {
      rhs->type_enum = lhs->type_enum;
      rhs->data_type = parser_copy_tree_list (parser, lhs->data_type);
      return rhs;
    }

      if (pt_assignment_class_compatible (parser, lhs, rhs) != NO_ERROR)
    {
      return NULL;
    }
    }
  else
    {
      SEMAN_COMPATIBLE_INFO sci = {
    0, PT_TYPE_NONE, 0, 0, false, pt_coll_infer (), NULL
      };
      bool is_cast_allowed = true;

      sci.type_enum = lhs->type_enum;
      if (lhs->data_type)
    {
      sci.prec = lhs->data_type->info.data_type.precision;
      sci.scale = lhs->data_type->info.data_type.dec_precision;
      if (lhs->type_enum == PT_TYPE_JSON && lhs->data_type->info.data_type.json_schema != NULL)
        {
          sci.force_cast = true;
        }
    }

      if (PT_HAS_COLLATION (lhs->type_enum))
    {
      (void) pt_get_collation_info (lhs, &(sci.coll_infer));
    }

      if (pt_is_compatible_without_cast (parser, &sci, rhs, &is_cast_allowed))
    {
      return rhs;
    }

      if (!is_cast_allowed)
    {
      return NULL;
    }

      if (rhs->type_enum != PT_TYPE_NULL)
    {
      if (rhs->type_enum == PT_TYPE_MAYBE)
        {
          TP_DOMAIN *d;

          if (lhs->type_enum == PT_TYPE_ENUMERATION)
        {
          d = pt_data_type_to_db_domain (parser, lhs->data_type, NULL);
          d = tp_domain_cache (d);
        }
          else
        {
          DB_TYPE lhs_dbtype;

          assert_release (!(PT_IS_STRING_TYPE (lhs->type_enum) || lhs->type_enum == PT_TYPE_NUMERIC)
                  || lhs->data_type != NULL);
          lhs_dbtype = pt_type_enum_to_db (lhs->type_enum);

          if (rhs->node_type != PT_HOST_VAR)
            {
              // TODO: It should be considered for parameterized types, refer to PT_IS_PARAMETERIZED_TYPE()
              if (lhs->type_enum == PT_TYPE_NUMERIC && lhs->data_type != NULL)
            {
              d = tp_domain_resolve (lhs_dbtype, NULL, sci.prec, sci.scale, NULL, 0);
            }
              else
            {
              d = tp_domain_resolve_default (lhs_dbtype);
            }
            }
          else
            {
              TP_DOMAIN *hv_domain = NULL;

              /* node_type is PT_HOST_VAR set host var's expected domain with next order of priority. 1.
               * host_var->host_var_expected_domains[index] 2. lhs's expected domain 3. default domain of lhs
               * type */
              if (rhs->info.host_var.index < parser->host_var_count && parser->host_var_expected_domains)
            {
              hv_domain = parser->host_var_expected_domains[rhs->info.host_var.index];
            }

              if (hv_domain && hv_domain->type->id != DB_TYPE_UNKNOWN)
            {
              /* host var's expected domain is already set */
              d = hv_domain;
              if (TP_TYPE_HAS_COLLATION (TP_DOMAIN_TYPE (d)))
                {
                  d->codeset = sci.coll_infer.codeset;
                  d->collation_id = sci.coll_infer.coll_id;
                }
            }
              else
            {
              /* use lhs's expected_domain */
              if (lhs->expected_domain != NULL)
                {
                  d = lhs->expected_domain;
                }
              else
                {
                  // TODO: It should be considered for parameterized types, refer to PT_IS_PARAMETERIZED_TYPE()
                  if (lhs->type_enum == PT_TYPE_NUMERIC && lhs->data_type != NULL)
                {
                  d = tp_domain_resolve (lhs_dbtype, NULL, sci.prec, sci.scale, NULL, 0);
                }
                  else
                {
                  d = tp_domain_resolve_default (lhs_dbtype);
                }
                }

              if (PT_HAS_COLLATION (lhs->type_enum))
                {
                  d = tp_domain_copy (d, false);
                  d->codeset = sci.coll_infer.codeset;
                  d->collation_id = sci.coll_infer.coll_id;
                  d = tp_domain_cache (d);
                }
            }
            }
        }

          pt_set_expected_domain (rhs, d);
          if (rhs->node_type == PT_HOST_VAR)
        {
          pt_preset_hostvar (parser, rhs);
        }
        }
      else
        {
          bool rhs_is_collection_with_str = false;
          PT_NODE *cast_dt = NULL;

          assert_release (!(PT_IS_STRING_TYPE (lhs->type_enum) || lhs->type_enum == PT_TYPE_NUMERIC)
                  || lhs->data_type != NULL);

          if (PT_IS_COLLECTION_TYPE (rhs->type_enum) && lhs->data_type == NULL && rhs->data_type != NULL)
        {
          PT_NODE *dt_element;

          dt_element = rhs->data_type;

          while (dt_element != NULL)
            {
              if (PT_IS_CHAR_STRING_TYPE (dt_element->type_enum))
            {
              rhs_is_collection_with_str = true;
              break;
            }
              dt_element = dt_element->next;
            }
        }

          if (PT_IS_COLLECTION_TYPE (lhs->type_enum) && PT_IS_COLLECTION_TYPE (rhs->type_enum)
          && lhs->data_type == NULL && rhs_is_collection_with_str == true)
        {
          PT_NODE *dt_element;

          /* create a data type for lhs with with string having DB charset */
          cast_dt = parser_copy_tree_list (parser, rhs->data_type);

          dt_element = cast_dt;

          while (dt_element != NULL)
            {
              if (PT_IS_CHAR_STRING_TYPE (dt_element->type_enum))
            {
              dt_element->info.data_type.collation_id = LANG_SYS_COLLATION;
              dt_element->info.data_type.units = lang_charset ();
              dt_element->info.data_type.collation_flag = TP_DOMAIN_COLL_NORMAL;
            }
              dt_element = dt_element->next;
            }
        }
          else
        {
          cast_dt = lhs->data_type;
        }

          rhs = pt_wrap_with_cast_op (parser, rhs, lhs->type_enum, 0, 0, cast_dt);
          if (cast_dt != NULL && cast_dt != lhs->data_type)
        {
          parser_free_tree (parser, cast_dt);
          cast_dt = NULL;
        }
          /* the call to pt_wrap_with_cast_op might fail because a call to allocate memory failed. In this case,
           * the error message is set by the calls inside pt_wrap_with_cast_op and we just return NULL */
        }
    }
    }

  return rhs;
}

/*
 * pt_check_assignments () - assert that the lhs of the set clause are
 *      all pt_name nodes.
 *      This will guarantee that there are no complex path expressions.
 *      Also asserts that the right hand side is assignment compatible.
 *   return:  none
 *   parser(in): the parser context
 *   stmt(in): an update or merge statement
 */

static void
pt_check_assignments (PARSER_CONTEXT * parser, PT_NODE * stmt)
{
  PT_NODE *a, *next, *lhs, *rhs, *list, *rhs_list;
  PT_NODE *assignment_list;
  bool dblinked;

  assert (parser != NULL);

  assignment_list = pt_get_assignments (stmt, &dblinked);
  if (assignment_list == NULL)
    {
      return;
    }

  for (a = assignment_list; a; a = next)
    {
      next = a->next;       /* save next link */
      if (a->node_type == PT_EXPR && a->info.expr.op == PT_ASSIGN && (lhs = a->info.expr.arg1) != NULL
      && (rhs = a->info.expr.arg2) != NULL)
    {
      if (lhs->node_type == PT_NAME)
        {
          if (pt_is_query (rhs))
        {
          /* check select list length */
          rhs_list = pt_get_select_list (parser, rhs);
          assert (rhs_list != NULL);
          if (pt_length_of_select_list (rhs_list, EXCLUDE_HIDDEN_COLUMNS) != 1)
            {
              /* e.g., a = (select 1, 2 from ...) */
              PT_ERRORm (parser, lhs, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ILLEGAL_RHS);
            }
          else if (!dblinked)   /* for local query only */
            {
              (void) pt_assignment_class_compatible (parser, lhs, rhs_list);
            }
        }
          else
        {
          /* Not a query, just check if assignment is possible. The call below will wrap the rhs node with a
           * cast to the type of the lhs_node */
          if (!dblinked)    /* for local query only */
            {
              (void) pt_assignment_class_compatible (parser, lhs, rhs);
            }
        }
        }
      else if (lhs->node_type == PT_EXPR && PT_IS_N_COLUMN_UPDATE_EXPR (lhs) && (list = lhs->info.expr.arg1))
        {
          /* multi-column update with subquery CASE1: always-false subquery is already converted NULL.  so, change
           * NULL into NULL paren-expr (a) = NULL -> a = NULL (a, b) = NULL -> a = NULL, b = NULL CASE2: (a, b) =
           * subquery */

          if (rhs->type_enum == PT_TYPE_NA || rhs->type_enum == PT_TYPE_NULL)
        {
          /* CASE 1: flatten multi-column assignment expr */
          PT_NODE *e1, *e1_next, *e2, *tmp;

          a->next = NULL;   /* cut-off expr link */
          lhs->info.expr.arg1 = NULL;   /* cut-off lhs link */

          parser_free_tree (parser, lhs);   /* free exp, arg1 */
          parser_free_tree (parser, rhs);   /* free exp, arg1 */

          e2 = parser_new_node (parser, PT_VALUE);
          if (e2 == NULL)
            {
              PT_ERRORm (parser, a, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
              return;
            }

          e2->type_enum = PT_TYPE_NULL;
          a->info.expr.arg1 = list;
          a->info.expr.arg2 = e2;
          e1 = list->next;
          list->next = NULL;
          tmp = NULL;   /* init */

          for (; e1; e1 = e1_next)
            {
              e1_next = e1->next;
              e1->next = NULL;

              e2 = parser_new_node (parser, PT_VALUE);
              if (e2 == NULL)
            {
              PT_ERRORm (parser, a, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
              return;
            }
              e2->type_enum = PT_TYPE_NULL;

              tmp = parser_new_node (parser, PT_EXPR);
              if (tmp == NULL)
            {
              PT_ERRORm (parser, a, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
              return;
            }

              tmp->info.expr.op = PT_ASSIGN;
              tmp->info.expr.arg1 = e1;
              tmp->info.expr.arg2 = e2;
              parser_append_node (tmp, a);
            }

          if (tmp == NULL)
            {
              a->next = next;   /* (a) = NULL */
            }
          else
            {
              tmp->next = next; /* (a, b) = NULL */
            }
        }
          else if (pt_is_query (rhs))
        {
          /* check select list length */
          rhs_list = pt_get_select_list (parser, rhs);
          assert (rhs_list != NULL);
          if (pt_length_of_list (list) != pt_length_of_select_list (rhs_list, EXCLUDE_HIDDEN_COLUMNS))
            {
              PT_ERRORm (parser, lhs, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ILLEGAL_LHS);
            }
          else
            {
              for (; list && rhs_list; rhs_list = rhs_list->next)
            {
              if (rhs_list->flag.is_hidden_column)
                {
                  /* skip hidden column */
                  continue;
                }

              (void) pt_assignment_class_compatible (parser, list, rhs_list);
              list = list->next;
            }
              assert (list == NULL);
              assert (rhs_list == NULL);
            }
        }
        }
      else
        {
          PT_ERRORm (parser, lhs, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ILLEGAL_LHS);
        }
    }
      else
    {
      /* malformed assignment list */
      PT_INTERNAL_ERROR (parser, "semantic");
    }
    }
}

/*
 * pt_replace_names_in_update_values () - Walk through update assignments and
 *                    replace references to attributes
 *                    with assignments for those
 *                    attributes
 * return : UPDATE statement or NULL
 * parser (in) : parser context
 * update (in) : UPDATE parse tree
 *
 *  Note: Update assignments are considered to be evaluate left to right.
 *   For each attribute referenced on the right side of an assignment, if the
 *   attribute was referenced in an assignment which appears in the UPDATE
 *   statement before this assignment (i.e.: to the left of this assigment)
 *   replace the value with that assignment.
 */
static PT_NODE *
pt_replace_names_in_update_values (PARSER_CONTEXT * parser, PT_NODE * update)
{
  PT_NODE *attr = NULL, *val = NULL;
  PT_NODE *node = NULL, *prev = NULL;

  if (!prm_get_bool_value (PRM_ID_UPDATE_USE_ATTRIBUTE_REFERENCES))
    {
      /* Use SQL standard approach which always uses the existing value for an attribute rather than the updated value */
      return update;
    }

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

  prev = update->info.update.assignment;
  if (prev == NULL)
    {
      return NULL;
    }

  if (prev->next == NULL)
    {
      /* only one assignment, nothing to be done */
      return update;
    }

  for (node = prev->next; node != NULL; prev = node, node = node->next)
    {
      if (!PT_IS_EXPR_NODE (node) || node->info.expr.op != PT_ASSIGN)
    {
      /* this is probably an error but it will be caught later */
      continue;
    }

      attr = node->info.expr.arg1;
      if (PT_IS_N_COLUMN_UPDATE_EXPR (attr))
    {
      /* Cannot reference other assignments in N_COLUMN_UPDATE_EXPR */
      continue;
    }

      val = node->info.expr.arg2;
      if (val == NULL || PT_IS_CONST (val))
    {
      /* nothing to be done for constants */
      continue;
    }

      /* This assignment is attr = expr. Walk expr and replace all occurrences of attributes with previous assignments.
       * Set prev->next to NULL so that we only search in assignments to the left of the current one. */
      prev->next = NULL;

      val =
    parser_walk_tree (parser, val, pt_replace_referenced_attributes, update->info.update.assignment, NULL, NULL);

      if (val != NULL && val != node->info.expr.arg2)
    {
      parser_free_tree (parser, node->info.expr.arg2);
      node->info.expr.arg2 = val;
    }

      /* repair node and list */
      prev->next = node;
    }

  return update;
}

/*
 * pt_replace_referenced_attributes () - replace an attributes with
 *                   expressions from previous assignments
 * return : node or NULL
 * parser (in) :
 * node (in) :
 * arg (in) :
 * continue_walk (in) :
 */
static PT_NODE *
pt_replace_referenced_attributes (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE *assignment = NULL, *val = NULL;
  PT_NODE *assignments = (PT_NODE *) arg;

  *continue_walk = PT_CONTINUE_WALK;

  if (!pt_is_attr (node))
    {
      return node;
    }

  if (node->node_type != PT_NAME)
    {
      /* this is a PT_DOT, we don't have to go further */
      *continue_walk = PT_LIST_WALK;
    }

  if (assignments == NULL)
    {
      assert_release (assignments != NULL);
      return node;
    }

  /* search for name in assignments */
  for (assignment = assignments; assignment != NULL; assignment = assignment->next)
    {
      if (PT_IS_N_COLUMN_UPDATE_EXPR (assignment))
    {
      continue;
    }

      if (pt_name_equal (parser, assignment->info.expr.arg1, node))
    {
      /* Found the attribute we're looking for */
      break;
    }
    }

  if (assignment == NULL)
    {
      /* Did not find the attribute. This means that UPDATE should use the existing value */
      return node;
    }

  /* Replace node with rhs from the assignment. Notice that we're stopping at the first encounter of this attribute.
   * Normally, we should find the last assignment (from left to right), but, since CUBRID does not allow multiple
   * assignments to the same attribute, the first occurrence is the only one. */
  val = parser_copy_tree (parser, assignment->info.expr.arg2);

  if (val == NULL)
    {
      /* set error and return original node */
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return node;
    }

  /* do not recurse into this node */
  *continue_walk = PT_LIST_WALK;

  return val;
}

/*
 * pt_no_attr_and_meta_attr_updates () - check for mixed (class, non-class)
 *    assignments in the same update/merge statement
 *   return:  none
 *   parser(in): the parser context
 *   stmt(in): an update/merge statement
 */
void
pt_no_attr_and_meta_attr_updates (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  bool has_attrib = false, has_meta_attrib = false;
  PT_ASSIGNMENTS_HELPER ea;
  PT_NODE *assignments;
  bool dblinked;

  assignments = pt_get_assignments (statement, &dblinked);
  if (assignments == NULL)
    {
      return;
    }

  pt_init_assignments_helper (parser, &ea, assignments);
  while (pt_get_next_assignment (&ea) && (!has_attrib || !has_meta_attrib))
    {
      if (ea.lhs->info.name.meta_class == PT_META_ATTR)
    {
      has_meta_attrib = true;
    }
      else
    {
      has_attrib = true;
    }
    }
  if (has_attrib && has_meta_attrib)
    {
      PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UPDATE_MIX_CLASS_NON_CLASS);
    }
}

/*
 * pt_node_double_insert_assignments () - Check if an attribute is assigned
 *                    more than once.
 *
 * return      : Void.
 * parser (in) : Parser context.
 * stmt (in)   : Insert statement.
 */
void
pt_no_double_insert_assignments (PARSER_CONTEXT * parser, PT_NODE * stmt)
{
  PT_NODE *attr = NULL, *spec = NULL;
  PT_NODE *entity_name;

  if (stmt == NULL || stmt->node_type != PT_INSERT)
    {
      return;
    }

  spec = stmt->info.insert.spec;
  entity_name = spec->info.spec.entity_name;
  if (entity_name == NULL)
    {
      assert (false);
      PT_ERROR (parser, stmt,
        "The parse tree of the insert statement is incorrect." " entity_name of spec must be set.");
      return;
    }

  if (entity_name->info.name.original == NULL)
    {
      assert (false);
      PT_ERROR (parser, entity_name, er_msg ());
      return;
    }

  /* check for duplicate assignments */
  for (attr = stmt->info.insert.attr_list; attr != NULL; attr = attr->next)
    {
      PT_NODE *attr2;
      for (attr2 = attr->next; attr2 != NULL; attr2 = attr2->next)
    {
      if (pt_name_equal (parser, attr, attr2))
        {
          PT_ERRORmf2 (parser, attr2, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_GT_1_ASSIGNMENT_TO,
               entity_name->info.name.original, attr2->info.name.original);
          return;
        }
    }
    }
}

/*
 * pt_no_double_updates () - assert that there are no multiple assignments to
 *      the same attribute in the given update or merge statement
 *   return:  none
 *   parser(in): the parser context
 *   stmt(in): an update or merge statement
 */

void
pt_no_double_updates (PARSER_CONTEXT * parser, PT_NODE * stmt)
{
  PT_NODE *a, *b, *att_a, *att_b;
  PT_NODE *assignment_list;
  bool dblinked;

  assert (parser != NULL);

  assignment_list = pt_get_assignments (stmt, &dblinked);
  if (assignment_list == NULL)
    {
      return;
    }

  for (a = assignment_list; a; a = a->next)
    {
      if (!(a->node_type == PT_EXPR && a->info.expr.op == PT_ASSIGN && (att_a = a->info.expr.arg1)))
    {
      goto exit_on_error;
    }

      if (att_a->node_type != PT_NAME)
    {
      if (PT_IS_N_COLUMN_UPDATE_EXPR (att_a))
        {
          att_a = att_a->info.expr.arg1;
        }
      else
        {
          goto exit_on_error;
        }
    }

      for (; att_a; att_a = att_a->next)
    {
      /* first, check current node */
      for (att_b = att_a->next; att_b; att_b = att_b->next)
        {
          if (att_b->node_type != PT_NAME || att_b->info.name.original == NULL)
        {
          goto exit_on_error;
        }
          /* for multi-table we must check name and spec id */
          if (!pt_str_compare (att_a->info.name.original, att_b->info.name.original, CASE_INSENSITIVE)
          && att_a->info.name.spec_id == att_b->info.name.spec_id)
        {
          PT_ERRORmf2 (parser, att_a, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_GT_1_ASSIGNMENT_TO,
                   att_a->info.name.resolved, att_a->info.name.original);
          return;
        }
        }

      /* then, check the following node */
      for (b = a->next; b; b = b->next)
        {
          if (!(b->node_type == PT_EXPR && b->info.expr.op == PT_ASSIGN && (att_b = b->info.expr.arg1)))
        {
          goto exit_on_error;
        }

          if (att_b->node_type != PT_NAME)
        {
          if (PT_IS_N_COLUMN_UPDATE_EXPR (att_b))
            {
              att_b = att_b->info.expr.arg1;
            }
          else
            {
              goto exit_on_error;
            }
        }

          for (; att_b; att_b = att_b->next)
        {
          if (att_b->node_type != PT_NAME || att_b->info.name.original == NULL)
            {
              goto exit_on_error;
            }
          /* for multi-table we must check name and spec id */
          if (!pt_str_compare (att_a->info.name.original, att_b->info.name.original, CASE_INSENSITIVE)
              && att_a->info.name.spec_id == att_b->info.name.spec_id)
            {
              PT_ERRORmf2 (parser, att_a, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_GT_1_ASSIGNMENT_TO,
                   att_a->info.name.resolved, att_a->info.name.original);
              return;
            }
        }
        }
    }
    }

  return;

exit_on_error:
  /* malformed assignment list */
  PT_INTERNAL_ERROR (parser, "semantic");
  return;
}

/*
 * pt_invert () -
 *   return:
 *   parser(in):
 *   name_expr(in): an expression from a select list
 *   result(out): written in terms of the same single variable or path-expr
 *
 * Note :
 * Given an expression p that involves only:
 *   + - / * ( ) constants and a single variable (which occurs only once).
 *
 * Find the functional inverse of the expression.
 * [ f and g are functional inverses if f(g(x)) == x ]
 *
 *       function       inverse
 *       --------       --------
 *          -x              -x
 *          4*x            x/4
 *          4*x+10         (x-10)/4
 *          6+x            x-6
 *
 * Can't invert:  x+y;  x+x;  x*x; constants ; count(*);  f(x) ;
 */
PT_NODE *
pt_invert (PARSER_CONTEXT * parser, PT_NODE * name_expr, PT_NODE * result)
{
  int result_isnull = 0;
  PT_NODE *tmp;
  PT_NODE *msgs;
  SEMANTIC_CHK_INFO sc_info = { NULL, NULL, 0, 0, 0, false, false };

  assert (parser != NULL);
  msgs = parser->error_msgs;

  /* find the variable and return if none */
  if (pt_find_var (name_expr, &tmp) != 1 || tmp == NULL)
    {
      return NULL;
    }

  /* walk through the expression, inverting as you go */
  while (name_expr)
    {
      /* Got a path expression, you're done. ( result = path expr ) */
      if (name_expr->node_type == PT_NAME)
    break;

      /* not an expression? then can't do it */
      if (name_expr->node_type != PT_EXPR)
    {
      result = 0;
      break;
    }

      /* the inverse of any expression involving NULL is NULL */
      result_isnull = result->type_enum == PT_TYPE_NULL;
      switch (name_expr->info.expr.op)
    {
    case PT_UNARY_MINUS:
      /* ( result = -expr ) <=> ( -result = expr ) */
      name_expr = name_expr->info.expr.arg1;
      if (!result_isnull)
        {
          tmp = parser_new_node (parser, PT_EXPR);
          if (tmp == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          return NULL;
        }

          tmp->info.expr.op = PT_UNARY_MINUS;
          tmp->info.expr.arg1 = result;
          if (tmp->info.expr.arg1->node_type == PT_EXPR)
        {
          tmp->info.expr.arg1->info.expr.paren_type = 1;
        }
          result = tmp;
        }
      break;

    case PT_PLUS:
      /* ( result = A + B ) <=> ( result - A = B ) */
      if (pt_find_var (name_expr->info.expr.arg1, 0))
        {
          if (result_isnull)
        {
          /* no need to invert result because result already has a null */
          name_expr = name_expr->info.expr.arg1;
        }
          else
        {
          tmp = parser_new_node (parser, PT_EXPR);
          if (tmp == NULL)
            {
              PT_INTERNAL_ERROR (parser, "allocate new node");
              return NULL;
            }

          tmp->info.expr.op = PT_MINUS;
          tmp->info.expr.arg1 = result;
          tmp->info.expr.arg2 = parser_copy_tree (parser, name_expr->info.expr.arg2);

          if (tmp->info.expr.arg2 == NULL)
            {
              PT_INTERNAL_ERROR (parser, "parser_copy_tree");
              return NULL;
            }

          if (tmp->info.expr.arg1->node_type == PT_EXPR)
            {
              tmp->info.expr.arg1->info.expr.paren_type = 1;
            }
          if (tmp->info.expr.arg2->node_type == PT_EXPR)
            {
              tmp->info.expr.arg2->info.expr.paren_type = 1;
            }
          name_expr = name_expr->info.expr.arg1;
          result = tmp;
        }
          break;
        }

      if (pt_find_var (name_expr->info.expr.arg2, 0))
        {
          if (result_isnull)
        {
          /* no need to invert result because result already has a null */
          name_expr = name_expr->info.expr.arg2;
        }
          else
        {
          tmp = parser_new_node (parser, PT_EXPR);
          if (tmp == NULL)
            {
              PT_INTERNAL_ERROR (parser, "allocate new node");
              return NULL;
            }

          tmp->info.expr.op = PT_MINUS;
          tmp->info.expr.arg1 = result;
          tmp->info.expr.arg2 = parser_copy_tree (parser, name_expr->info.expr.arg1);
          if (tmp->info.expr.arg2 == NULL)
            {
              PT_INTERNAL_ERROR (parser, "parser_copy_tree");
              return NULL;
            }

          if (tmp->info.expr.arg1->node_type == PT_EXPR)
            {
              tmp->info.expr.arg1->info.expr.paren_type = 1;
            }
          if (tmp->info.expr.arg2->node_type == PT_EXPR)
            {
              tmp->info.expr.arg2->info.expr.paren_type = 1;
            }
          name_expr = name_expr->info.expr.arg2;
          result = tmp;
        }
          break;
        }

      return NULL;

    case PT_MINUS:
      /* ( result = A-B ) <=> ( result+B = A ) ( result = A-B ) <=> ( A-result = B ) */
      if (pt_find_var (name_expr->info.expr.arg1, 0))
        {
          if (result_isnull)
        {
          /* no need to invert result because result already has a null */
          name_expr = name_expr->info.expr.arg1;
        }
          else
        {
          tmp = parser_new_node (parser, PT_EXPR);
          if (tmp == NULL)
            {
              PT_INTERNAL_ERROR (parser, "allocate new node");
              return NULL;
            }

          tmp->info.expr.op = PT_PLUS;
          tmp->info.expr.arg1 = result;
          tmp->info.expr.arg2 = parser_copy_tree (parser, name_expr->info.expr.arg2);
          if (tmp->info.expr.arg2 == NULL)
            {
              PT_INTERNAL_ERROR (parser, "parser_copy_tree");
              return NULL;
            }

          if (tmp->info.expr.arg1->node_type == PT_EXPR)
            {
              tmp->info.expr.arg1->info.expr.paren_type = 1;
            }
          if (tmp->info.expr.arg2->node_type == PT_EXPR)
            {
              tmp->info.expr.arg2->info.expr.paren_type = 1;
            }
          name_expr = name_expr->info.expr.arg1;
          result = tmp;
        }
          break;
        }

      if (pt_find_var (name_expr->info.expr.arg2, 0))
        {
          if (result_isnull)
        {
          /* no need to invert result because result already has a null */
          name_expr = name_expr->info.expr.arg2;
        }
          else
        {
          tmp = parser_new_node (parser, PT_EXPR);
          if (tmp == NULL)
            {
              PT_INTERNAL_ERROR (parser, "allocate new node");
              return NULL;
            }

          tmp->info.expr.op = PT_MINUS;
          tmp->info.expr.arg2 = result;
          tmp->info.expr.arg1 = parser_copy_tree (parser, name_expr->info.expr.arg1);
          if (tmp->info.expr.arg1 == NULL)
            {
              PT_INTERNAL_ERROR (parser, "parser_copy_tree");
              return NULL;
            }

          if (tmp->info.expr.arg1->node_type == PT_EXPR)
            {
              tmp->info.expr.arg1->info.expr.paren_type = 1;
            }
          if (tmp->info.expr.arg2->node_type == PT_EXPR)
            {
              tmp->info.expr.arg2->info.expr.paren_type = 1;
            }
          name_expr = name_expr->info.expr.arg2;
          result = tmp;
        }
          break;
        }

      return NULL;

    case PT_DIVIDE:
      /* ( result = A/B ) <=> ( result*B = A ) ( result = A/B ) <=> ( A/result = B ) */
      if (pt_find_var (name_expr->info.expr.arg1, 0))
        {
          if (result_isnull)
        {
          /* no need to invert result because result already has a null */
          name_expr = name_expr->info.expr.arg1;
        }
          else
        {
          tmp = parser_new_node (parser, PT_EXPR);
          if (tmp == NULL)
            {
              PT_INTERNAL_ERROR (parser, "allocate new node");
              return NULL;
            }

          tmp->info.expr.op = PT_TIMES;
          tmp->info.expr.arg1 = result;
          tmp->info.expr.arg2 = parser_copy_tree (parser, name_expr->info.expr.arg2);
          if (tmp->info.expr.arg2 == NULL)
            {
              PT_INTERNAL_ERROR (parser, "parser_copy_tree");
              return NULL;
            }

          if (tmp->info.expr.arg1->node_type == PT_EXPR)
            {
              tmp->info.expr.arg1->info.expr.paren_type = 1;
            }
          if (tmp->info.expr.arg2->node_type == PT_EXPR)
            {
              tmp->info.expr.arg2->info.expr.paren_type = 1;
            }
          name_expr = name_expr->info.expr.arg1;
          result = tmp;
        }
          break;
        }

      if (pt_find_var (name_expr->info.expr.arg2, 0))
        {
          if (result_isnull)
        {
          /* no need to invert result because result already has a null */
          name_expr = name_expr->info.expr.arg2;
        }
          else
        {
          tmp = parser_new_node (parser, PT_EXPR);
          if (tmp == NULL)
            {
              PT_INTERNAL_ERROR (parser, "allocate new node");
              return NULL;
            }

          tmp->info.expr.op = PT_DIVIDE;
          tmp->info.expr.arg2 = result;
          tmp->info.expr.arg1 = parser_copy_tree (parser, name_expr->info.expr.arg1);
          if (tmp->info.expr.arg1 == NULL)
            {
              PT_INTERNAL_ERROR (parser, "parser_copy_tree");
              return NULL;
            }

          if (tmp->info.expr.arg1->node_type == PT_EXPR)
            {
              tmp->info.expr.arg1->info.expr.paren_type = 1;
            }
          if (tmp->info.expr.arg2->node_type == PT_EXPR)
            {
              tmp->info.expr.arg2->info.expr.paren_type = 1;
            }
          name_expr = name_expr->info.expr.arg2;
          result = tmp;
        }
          break;
        }

      return NULL;

    case PT_TIMES:
      /* ( result = A*B ) <=> ( result/A = B ) */
      if (pt_find_var (name_expr->info.expr.arg1, 0))
        {
          if (result_isnull)
        {
          /* no need to invert result because result already has a null */
          name_expr = name_expr->info.expr.arg1;
        }
          else
        {
          tmp = parser_new_node (parser, PT_EXPR);
          if (tmp == NULL)
            {
              PT_INTERNAL_ERROR (parser, "allocate new node");
              return NULL;
            }

          tmp->info.expr.op = PT_DIVIDE;
          tmp->info.expr.arg1 = result;
          tmp->info.expr.arg2 = parser_copy_tree (parser, name_expr->info.expr.arg2);
          if (tmp->info.expr.arg2 == NULL)
            {
              PT_INTERNAL_ERROR (parser, "parser_copy_tree");
              return NULL;
            }

          if (tmp->info.expr.arg1->node_type == PT_EXPR)
            {
              tmp->info.expr.arg1->info.expr.paren_type = 1;
            }
          if (tmp->info.expr.arg2->node_type == PT_EXPR)
            {
              tmp->info.expr.arg2->info.expr.paren_type = 1;
            }
          name_expr = name_expr->info.expr.arg1;
          result = tmp;
        }
          break;
        }

      if (pt_find_var (name_expr->info.expr.arg2, 0))
        {
          if (result_isnull)
        {
          /* no need to invert result because result already has a null */
          name_expr = name_expr->info.expr.arg2;
        }
          else
        {
          tmp = parser_new_node (parser, PT_EXPR);
          if (tmp == NULL)
            {
              PT_INTERNAL_ERROR (parser, "allocate new node");
              return NULL;
            }

          tmp->info.expr.op = PT_DIVIDE;
          tmp->info.expr.arg1 = result;
          tmp->info.expr.arg2 = parser_copy_tree (parser, name_expr->info.expr.arg1);
          if (tmp->info.expr.arg2 == NULL)
            {
              PT_INTERNAL_ERROR (parser, "parser_copy_tree");
              return NULL;
            }

          if (tmp->info.expr.arg1->node_type == PT_EXPR)
            {
              tmp->info.expr.arg1->info.expr.paren_type = 1;
            }
          if (tmp->info.expr.arg2->node_type == PT_EXPR)
            {
              tmp->info.expr.arg2->info.expr.paren_type = 1;
            }
          name_expr = name_expr->info.expr.arg2;
          result = tmp;
        }
          break;
        }

      return NULL;

    case PT_CAST:
      /* special case */
      name_expr = name_expr->info.expr.arg1;
      break;

    default:
      return NULL;
    }
    }

  /* set type of expression */
  if (!result_isnull)
    {
      sc_info.top_node = name_expr;
      sc_info.donot_fold = false;
      result = pt_semantic_type (parser, result, &sc_info);
    }

  if (result)
    {
      /* return name and resulting expression */
      result->next = parser_copy_tree (parser, name_expr);
    }

  if (pt_has_error (parser))
    {
      /* if we got an error just indicate not-invertible, end return with previous error state. */
      parser->error_msgs = msgs;
      return NULL;
    }

  return result;
}

/*
 * pt_find_var () - Explores an expression looking for a path expr.
 *                  Count these and return the count
 *   return: number of path (PT_NAME node) expressions in the tree
 *   p(in): an parse tree representing the syntactic
 *   result(out): for returning a result expression pointer
 */

int
pt_find_var (PT_NODE * p, PT_NODE ** result)
{
  if (!p)
    return 0;

  /* got a name expression */
  if (p->node_type == PT_NAME || (p->node_type == PT_DOT_))
    {
      if (result)
    *result = p;
      return 1;
    }

  /* if an expr (binary op) count both paths */
  if (p->node_type == PT_EXPR)
    {
      return (pt_find_var (p->info.expr.arg1, result) + pt_find_var (p->info.expr.arg2, result));
    }

  return 0;
}

/*
 * pt_remove_from_list () -
 *   return: PT_NODE* to the list without "node" in it
 *   parser(in):
 *   node(in/out):
 *   list(in/out):
 */
PT_NODE *
pt_remove_from_list (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * list)
{
  PT_NODE *temp;

  if (!list)
    return list;

  if (node == list)
    {
      temp = node->next;
      node->next = NULL;
      parser_free_tree (parser, node);
      return temp;
    }

  temp = list;
  while (temp && temp->next != node)
    {
      temp = temp->next;
    }

  if (temp)
    {
      temp->next = node->next;
      node->next = NULL;
      parser_free_tree (parser, node);
    }

  return list;
}

/*
 * pt_find_order_value_in_list () - checking an ORDER_BY list for a node with
 *                                  the same value as sort_spec
 *   return: PT_NODE* the found match or NULL
 *   parser(in):
 *   sort_value(in):
 *   order_list(in):
 */

PT_NODE *
pt_find_order_value_in_list (PARSER_CONTEXT * parser, const PT_NODE * sort_value, const PT_NODE * order_list)
{
  PT_NODE *match = NULL;

  match = (PT_NODE *) order_list;

  while (match && sort_value && match->info.sort_spec.expr)
    {
      if (sort_value->node_type == PT_VALUE && match->info.sort_spec.expr->node_type == PT_VALUE
      && (match->info.sort_spec.expr->info.value.data_value.i == sort_value->info.value.data_value.i))
    {
      break;
    }
      else if (sort_value->node_type == PT_NAME && match->info.sort_spec.expr->node_type == PT_NAME
           && (pt_check_path_eq (parser, sort_value, match->info.sort_spec.expr) == 0))
    {
      /* when create/alter view, columns which are not in select list will not be replaced as value type. */
      break;
    }
      else
    {
      match = match->next;
    }
    }

  return match;
}

/*
 * pt_check_order_by () - checking an ORDER_BY clause
 *   return:
 *   parser(in):
 *   query(in): query node has ORDER BY
 *
 * Note :
 * If it is an INTEGER, make sure it does not exceed the number of items
 * in the select list.
 * If it is a path expression, match it with an item in the select list and
 * replace it with the corresponding INTEGER.
 * IF not match, add hidden_column to select_list.
 * For the order-by clause of a UNION/INTERSECTION type query,
 * simply check that the items are ALL INTEGERS and it does not
 * exceed the number of items in the select list..
 */

int
pt_check_order_by (PARSER_CONTEXT * parser, PT_NODE * query)
{
  PT_NODE *select_list, *order_by, *col, *r, *temp, *order, *match;
  int n, i, select_list_len, select_list_full_len;
  bool ordbynum_flag;
  char *r_str = NULL;
  int error;
  bool skip_orderby_num = false;

  /* initinalize local variables */
  error = NO_ERROR;
  select_list = order_by = NULL;

  /* get select_list */
  switch (query->node_type)
    {
    case PT_SELECT:
      select_list = query->info.query.q.select.list;
      break;

    case PT_UNION:
    case PT_INTERSECTION:
    case PT_DIFFERENCE:
      {
    PT_NODE *arg1, *arg2;

    /* traverse through nested union */
    temp = query;
    while (1)
      {
        arg1 = temp->info.query.q.union_.arg1;
        arg2 = temp->info.query.q.union_.arg2;

        if (PT_IS_QUERY (arg1))
          {
        if (arg1->node_type == PT_SELECT)
          {     /* found, exit loop */
            select_list = arg1->info.query.q.select.list;
            break;
          }
        else
          {
            temp = arg1;    /* continue */
          }
          }
        else
          {
        /* should not get here, that is an error! */
        error = MSGCAT_SEMANTIC_UNION_INCOMPATIBLE;
        PT_ERRORmf2 (parser, arg1, MSGCAT_SET_PARSER_SEMANTIC, error, pt_short_print (parser, arg1),
                 pt_short_print (parser, arg2));
        break;
          }
      }
      }
      break;

    default:
      break;
    }

  /* not query statement or error occurs */
  if (select_list == NULL)
    {
      return error;
    }

  if (query->node_type == PT_SELECT && pt_is_single_tuple (parser, query))
    {
      /*
       * This case means "select count(*) from athlete order by code"
       * we will remove order by clause to avoid error message
       * but, "select count(*) from athlete order by 2" should make out of range err
       */
      if (query->info.query.order_by != NULL)
    {
      PT_NODE head;
      PT_NODE *last = &head;
      PT_NODE *order_by = query->info.query.order_by;

      last->next = NULL;
      while (order_by != NULL)
        {
          PT_NODE *next = order_by->next;
          order_by->next = NULL;

          if (order_by->info.sort_spec.expr->node_type == PT_NAME
          || order_by->info.sort_spec.expr->node_type == PT_EXPR)
        {
          parser_free_node (parser, order_by);
          skip_orderby_num = true;
        }
          else
        {
          // leave PT_VALUE, PT_HOST_VAR, in fact, PT_HOST_VAR will be rejected soon.
          last->next = order_by;
          last = order_by;
        }

          order_by = next;
        }

      query->info.query.order_by = head.next;
    }

      /*
       * This case means "select count(*) from athlete limit ?"
       * This limit clause should be evaluated after "select count(*) from athlete"
       * So we will change it as subquery.
       */
      if (query->info.query.limit != NULL)
    {
      SEMANTIC_CHK_INFO sc_info = { NULL, NULL, 0, 0, 0, false, false };
      PT_NODE *limit = query->info.query.limit;
      query->info.query.limit = NULL;

      /* rewrite as derived table */
      query = mq_rewrite_aggregate_as_derived (parser, query);
      if (query == NULL || pt_has_error (parser))
        {
          return ER_FAILED;
        }

      /* clear spec_ids of names referring derived table and re-run name resolving; their types are not resolved
       * and, since we're on the post stage of the tree walk, this will not happen naturally; */
      query->info.query.q.select.list =
        mq_clear_ids (parser, query->info.query.q.select.list, query->info.query.q.select.from);

      /* re-resolve names */
      sc_info.donot_fold = true;
      sc_info.top_node = query;
      query = pt_resolve_names (parser, query, &sc_info);
      if (pt_has_error (parser) || !query)
        {
          return ER_FAILED;
        }

      query->info.query.limit = limit;
    }
    }

  /* get ORDER BY clause */
  order_by = query->info.query.order_by;
  if (order_by == NULL)
    {
      if (query->node_type == PT_SELECT)
    {
      /* need to check select_list */
      goto check_select_list;
    }

      /* union has not ORDER BY */
      return error;
    }

  /* save original length of select_list */
  select_list_len = 0;
  select_list_full_len = pt_length_of_select_list (select_list, INCLUDE_HIDDEN_COLUMNS);
#if !defined (NDEBUG)
  select_list_len = pt_length_of_select_list (select_list, EXCLUDE_HIDDEN_COLUMNS);
#endif /* !NDEBUG */

  for (order = order_by; order; order = order->next)
    {
      /* get the EXPR */
      r = order->info.sort_spec.expr;
      if (r == NULL)
    {           /* impossible case */
      continue;
    }

      /* if a good integer, done */
      if (r->node_type == PT_VALUE)
    {
      if (r->type_enum == PT_TYPE_INTEGER)
        {
          n = r->info.value.data_value.i;
          /* check size of the integer */
          if (select_list_full_len < n || n < 1)
        {
          error = MSGCAT_SEMANTIC_SORT_SPEC_RANGE_ERR;
          PT_ERRORmf (parser, r, MSGCAT_SET_PARSER_SEMANTIC, error, n);
          /* go ahead */
        }
          else
        {
          /* the following invalid query cause error in here SELECT orderby_num() FROM t ORDER BY 1; */
          for (col = select_list, i = 1; i < n; i++)
            {
              col = col->next;
            }

          /* sorting node should be either an existing select node or an hidden column added by system */
          assert (n <= select_list_len || col->flag.is_hidden_column);

          if (col->node_type == PT_EXPR && col->info.expr.op == PT_ORDERBY_NUM)
            {
              error = MSGCAT_SEMANTIC_SORT_SPEC_NAN_PATH;
              PT_ERRORmf (parser, col, MSGCAT_SET_PARSER_SEMANTIC, error, "ORDERBY_NUM()");
              /* go ahead */
            }
        }
        }
      else
        {
          error = MSGCAT_SEMANTIC_SORT_SPEC_WANT_NUM;
          PT_ERRORm (parser, r, MSGCAT_SET_PARSER_SEMANTIC, error);
          /* go ahead */
        }
    }
      else if (r->node_type == PT_HOST_VAR)
    {
      PT_ERRORmf (parser, r, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NO_ORDERBY_ALLOWED,
              pt_short_print (parser, r));
      error = MSGCAT_SEMANTIC_NO_ORDERBY_ALLOWED;
    }
      else
    {
      /* not an integer value. Try to match with something in the select list. */

      n = 1;        /* a counter for position in select_list */
      if (r->node_type != PT_NAME && r->node_type != PT_DOT_)
        {
          r_str = parser_print_tree (parser, r);
        }

      for (col = select_list; col; col = col->next)
        {
          /* if match, break; */
          if (r->node_type == col->node_type)
        {
          if (r->node_type == PT_NAME || r->node_type == PT_DOT_)
            {
              if (pt_check_path_eq (parser, r, col) == 0)
            {
              break;    /* match */
            }
            }
          else
            {
              if (pt_str_compare (r_str, parser_print_tree (parser, col), CASE_INSENSITIVE) == 0)
            {
              break;    /* match */
            }
            }
        }
          else if (pt_check_compatible_node_for_orderby (parser, r, col))
        {
          break;
        }
          n++;
        }

      /* if end of list, no match create a hidden column node and append to select_list */
      if (col == NULL)
        {
          if (query->node_type != PT_SELECT)
        {
          error = MSGCAT_SEMANTIC_ORDERBY_IS_NOT_INT;
          PT_ERRORm (parser, r, MSGCAT_SET_PARSER_SEMANTIC, error);
        }
          else if (query->info.query.all_distinct == PT_DISTINCT)
        {
          error = MSGCAT_SEMANTIC_INVALID_ORDERBY_WITH_DISTINCT;
          PT_ERRORm (parser, r, MSGCAT_SET_PARSER_SEMANTIC, error);
        }
          else
        {
          /* when check order by clause in create/alter view, do not change order_by and select_list. The order
           * by clause will be replaced in mq_translate_subqueries() again. */
          if (query->flag.do_not_replace_orderby)
            {
              continue;
            }

          col = parser_copy_tree (parser, r);
          if (col == NULL)
            {
              error = MSGCAT_SEMANTIC_OUT_OF_MEMORY;
              PT_ERRORm (parser, r, MSGCAT_SET_PARSER_SEMANTIC, error);
              return error; /* give up */
            }
          else
            {
              /* mark as a hidden column */
              col->flag.is_hidden_column = 1;
              parser_append_node (col, select_list);
            }
        }
        }

      /* we got a match=n, Create a value node and replace expr with it */
      temp = parser_new_node (parser, PT_VALUE);
      if (temp == NULL)
        {
          error = MSGCAT_SEMANTIC_OUT_OF_MEMORY;
          PT_ERRORm (parser, r, MSGCAT_SET_PARSER_SEMANTIC, error);
        }
      else
        {
          temp->type_enum = PT_TYPE_INTEGER;
          temp->info.value.data_value.i = n;
          pt_value_to_db (parser, temp);
          parser_free_tree (parser, r);
          order->info.sort_spec.expr = temp;
        }
    }

      if (error != NO_ERROR)
    {           /* something wrong */
      continue;     /* go ahead */
    }

      /* set order by position num */
      order->info.sort_spec.pos_descr.pos_no = n;
      if (query->node_type != PT_SELECT)
    {
      continue;     /* OK */
    }

      /* at here, query->node_type == PT_SELECT set order_by domain info */
      if (col->type_enum != PT_TYPE_NONE && col->type_enum != PT_TYPE_MAYBE)
    {           /* is resolved */
      order->info.sort_spec.pos_descr.dom = pt_xasl_node_to_domain (parser, col);
    }
    }               /* for (order = order_by; ...) */

  /* now check for duplicate entries.  - If they match on ascending/descending, remove the second.  - If they do not,
   * generate an error. */
  for (order = order_by; order; order = order->next)
    {
      while ((match = pt_find_order_value_in_list (parser, order->info.sort_spec.expr, order->next)))
    {
      if ((order->info.sort_spec.asc_or_desc != match->info.sort_spec.asc_or_desc)
          || (pt_to_null_ordering (order) != pt_to_null_ordering (match)))
        {
          error = MSGCAT_SEMANTIC_SORT_DIR_CONFLICT;
          PT_ERRORmf (parser, match, MSGCAT_SET_PARSER_SEMANTIC, error, pt_short_print (parser, match));
          break;
        }
      else
        {
          order->next = pt_remove_from_list (parser, match, order->next);
        }
    }
    }

  if (error != NO_ERROR)
    {               /* give up */
      return error;
    }

check_select_list:

  /* orderby_num() in select list restriction check */
  for (col = select_list; col; col = col->next)
    {
      if (PT_IS_QUERY_NODE_TYPE (col->node_type))
    {
      /* skip orderby_num() expression in subqueries */
      continue;
    }

      if (col->node_type == PT_EXPR && col->info.expr.op == PT_ORDERBY_NUM)
    {
      if (!order_by && !skip_orderby_num)
        {
          /* the following invalid query cause error in here; SELECT orderby_num() FROM t; */
          error = MSGCAT_SEMANTIC_SORT_SPEC_NOT_EXIST;
          PT_ERRORmf (parser, col, MSGCAT_SET_PARSER_SEMANTIC, error, "ORDERBY_NUM()");
          break;
        }
    }
      else
    {
      /* the following query cause error in here; SELECT orderby_num()+1 FROM t; SELECT orderby_num()+1, a FROM t
       * ORDER BY 2; SELECT {orderby_num()} FROM t; SELECT {orderby_num()+1}, a FROM t ORDER BY 2; */
      ordbynum_flag = false;
      (void) parser_walk_leaves (parser, col, pt_check_orderbynum_pre, NULL, pt_check_orderbynum_post,
                     &ordbynum_flag);
      if (ordbynum_flag)
        {
          error = MSGCAT_SEMANTIC_ORDERBYNUM_SELECT_LIST_ERR;
          PT_ERRORm (parser, col, MSGCAT_SET_PARSER_SEMANTIC, error);
          break;
        }
    }
    }

  return error;
}

/*
 * pt_check_order_by () - checking an GROUP_BY clause
 *   return:
 *   parser(in):
 *   query(in): query node has GROUP BY
 *
 * Note :
 * 1. If a position is specified on group by clause, rewrite the position to name or expression.
 * 2. check that grouped things are single valued.
 * 3. do not allow GROUP BY ... WITH ROLLUP and GROUPBY_NUM ()
 * 4. CONNECT_BY_ROOT, SYS_CONNECT_BY_PATH cannot be written with GROUP_BY
 */

int
pt_check_group_by (PARSER_CONTEXT * parser, PT_NODE * node)
{
  int error = NO_ERROR;
  PT_NODE *t_node;

  /* If a position is specified on group by clause, rewrite the position to name or expression. */
  if (pt_has_aggregate (parser, node))
    {
      PT_AGG_CHECK_INFO info;
      PT_NODE *r;
      QFILE_TUPLE_VALUE_POSITION pos;
      PT_NODE *referred_node;
      int max_position;

      /* STEP 1: init agg info */
      info.from = node->info.query.q.select.from;
      info.depth = 0;
      info.group_by = node->info.query.q.select.group_by;

      max_position = pt_length_of_select_list (node->info.query.q.select.list, EXCLUDE_HIDDEN_COLUMNS);

      /* If a position is specified on group by clause, rewrite the position to name or expression */
      for (t_node = info.group_by; t_node; t_node = t_node->next)
    {
      r = t_node->info.sort_spec.expr;
      if (r == NULL)
        {
          continue;
        }
      /*
       * If a position is specified on group by clause,
       * we should check its range.
       */
      if (r->node_type == PT_VALUE && r->alias_print == NULL)
        {
          if (r->type_enum != PT_TYPE_INTEGER)
        {
          PT_ERRORm (parser, r, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SORT_SPEC_WANT_NUM);
          continue;
        }
          else if (r->info.value.data_value.i == 0 || r->info.value.data_value.i > max_position)
        {
          PT_ERRORmf (parser, r, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SORT_SPEC_RANGE_ERR,
                  r->info.value.data_value.i);
          continue;
        }
        }
      else if (r->node_type == PT_HOST_VAR)
        {
          PT_ERRORmf (parser, r, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NO_GROUPBY_ALLOWED,
              pt_short_print (parser, r));
          continue;
        }

      /* check for after group by position */
      pt_to_pos_descr (parser, &pos, r, node, &referred_node, false);
      if (pos.pos_no > 0)
        {
          /* set after group by position num, domain info */
          t_node->info.sort_spec.pos_descr = pos;
        }
      /*
       * If there is a node referred by the position,
       * we should rewrite the position to real name or expression
       * regardless of pos.pos_no.
       */
      if (referred_node != NULL)
        {
          t_node->info.sort_spec.expr = parser_copy_tree (parser, referred_node);
          parser_free_node (parser, r);
        }
    }

      /* STEP 2: check that grouped things are single valued */
      if (prm_get_bool_value (PRM_ID_ONLY_FULL_GROUP_BY) || node->info.query.q.select.group_by == NULL)
    {
      (void) parser_walk_tree (parser, node->info.query.q.select.list, pt_check_single_valued_node, &info,
                   pt_check_single_valued_node_post, &info);
      (void) parser_walk_tree (parser, node->info.query.q.select.having, pt_check_single_valued_node, &info,
                   pt_check_single_valued_node_post, &info);
    }
    }

  /* do not allow GROUP BY ... WITH ROLLUP and GROUPBY_NUM () */
  if (node->info.query.q.select.group_by != NULL && node->info.query.q.select.group_by->flag.with_rollup)
    {
      bool has_gbynum = false;
      (void) parser_walk_tree (parser, node->info.query.q.select.having, pt_check_groupbynum_pre, NULL,
                   pt_check_groupbynum_post, (void *) &has_gbynum);

      if (has_gbynum)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CANNOT_USE_GROUPBYNUM_WITH_ROLLUP);
    }
    }

  /* CONNECT_BY_ROOT, SYS_CONNECT_BY_PATH cannot be written with GROUP_BY */
  if (node->info.query.q.select.group_by != NULL)
    {
      int disallow_ops[] = {
    2,          /* number of operators */
    PT_CONNECT_BY_ROOT,
    PT_SYS_CONNECT_BY_PATH,
      };
      parser_walk_tree (parser, node->info.query.q.select.list, pt_expr_disallow_op_except_agg, disallow_ops,
            NULL, NULL);

      if (node->info.query.order_by != NULL)
    {
      parser_walk_tree (parser, node->info.query.order_by, pt_expr_disallow_op_except_agg, disallow_ops,
                NULL, NULL);
    }
    }

  return error;
}

/*
 * pt_check_path_eq () - determine if two path expressions are the same
 *   return: 0 if two path expressions are the same, else non-zero.
 *   parser(in):
 *   p(in):
 *   q(in):
 */
int
pt_check_path_eq (PARSER_CONTEXT * parser, const PT_NODE * p, const PT_NODE * q)
{
  PT_NODE_TYPE n;

  if (p == NULL && q == NULL)
    {
      return 0;
    }

  if (p == NULL || q == NULL)
    {
      return 1;
    }

  CAST_POINTER_TO_NODE (p);
  CAST_POINTER_TO_NODE (q);

  /* check node types are same */
  if (p->node_type != q->node_type)
    {
      return 1;
    }

  n = p->node_type;
  switch (n)
    {
      /*
       * if a name, the original and resolved fields must match
       *
       * In order to distinguish User Schema, the original, resolved name may contain a user name
       * with a dot(.) as a separator. It is not necessary to attach a user name to its own table,
       * but the user name currently connected internally is attached. So, when comparing original and resolved names,
       * we need to compare names after dot(.).
       *
       */
    case PT_NAME:
      if (p->info.name.spec_id != q->info.name.spec_id)
    {
      return 1;
    }
      if (pt_user_specified_name_compare (p->info.name.original, q->info.name.original))
    {
      return 1;
    }
      if (pt_user_specified_name_compare (p->info.name.resolved, q->info.name.resolved))
    {
      return 1;
    }
      break;            /* PT_NAME */

      /* EXPR must be X.Y.Z. */
    case PT_DOT_:
      if (pt_check_path_eq (parser, p->info.dot.arg1, q->info.dot.arg1))
    {
      return 1;
    }

      /* A recursive call on arg2 should work, except that we have not yet recognised common sub-path expressions
       * However, it is also sufficient and true that the left path be strictly equal and arg2's names match. That even
       * allows us to use this very function to implement recognition of common path expressions. */
      if (p->info.dot.arg2 == NULL || q->info.dot.arg2 == NULL)
    {
      return 1;
    }

      if (p->info.dot.arg2->node_type != PT_NAME || q->info.dot.arg2->node_type != PT_NAME)
    {
      return 1;
    }

      if (pt_str_compare (p->info.dot.arg2->info.name.original, q->info.dot.arg2->info.name.original, CASE_INSENSITIVE))
    {
      return 1;
    }

      break;            /* PT_DOT_ */

    case PT_EXPR:
      {
    if (p->info.expr.op != q->info.expr.op)
      {
        return 1;
      }

    if (pt_check_path_eq (parser, p->info.expr.arg1, q->info.expr.arg1))
      {
        return 1;
      }

    if (pt_check_path_eq (parser, p->info.expr.arg2, q->info.expr.arg2))
      {
        return 1;
      }

    if (pt_check_path_eq (parser, p->info.expr.arg3, q->info.expr.arg3))
      {
        return 1;
      }
      }
      break;            /* PT_EXPR */

    case PT_VALUE:
      {
    const char *p_str = NULL;
    const char *q_str = NULL;
    unsigned int save_custom;

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

    p_str = parser_print_tree (parser, p);
    q_str = parser_print_tree (parser, q);

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

    if (pt_str_compare (p_str, q_str, CASE_INSENSITIVE))
      {
        return 1;
      }
      }
      break;            /* PT_VALUE */

    default:
      PT_ERRORmf (parser, p, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SORT_SPEC_NAN_PATH,
          pt_short_print (parser, p));
      return 1;
    }

  return 0;
}

/*
 * pt_check_class_eq () - determine if two class name expressions are the same
 *   return: 0 if two class name expressions are the same, else non-zero
 *   parser(in):
 *   p(in):
 *   q(in):
 */
int
pt_check_class_eq (PARSER_CONTEXT * parser, PT_NODE * p, PT_NODE * q)
{
  PT_NODE_TYPE n;

  if (p == NULL && q == NULL)
    {
      return 0;
    }

  if (p == NULL || q == NULL)
    {
      return 1;
    }

  /* check if node types are same */
  if (p->node_type != q->node_type)
    {
      return 1;
    }

  n = p->node_type;
  switch (n)
    {
      /* if a name, the resolved (class name) fields must match */
    case PT_NAME:
      if (p->info.name.spec_id != q->info.name.spec_id)
    {
      return 1;
    }
      if (pt_str_compare (p->info.name.resolved, q->info.name.resolved, CASE_INSENSITIVE))
    {
      return 1;
    }
      break;            /* PT_NAME */

    case PT_EXPR:
      {
    if (p->info.expr.op != q->info.expr.op)
      {
        return 1;
      }

    if (pt_check_class_eq (parser, p->info.expr.arg1, q->info.expr.arg1))
      {
        return 1;
      }

    if (pt_check_class_eq (parser, p->info.expr.arg2, q->info.expr.arg2))
      {
        return 1;
      }

    if (pt_check_class_eq (parser, p->info.expr.arg3, q->info.expr.arg3))
      {
        return 1;
      }
      }
      break;            /* PT_EXPR */

    default:
      PT_ERRORmf (parser, p, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SORT_SPEC_NAN_PATH,
          pt_short_print (parser, p));
      return 1;
    }

  return 0;
}

/*
 * pt_coerce_insert_values () - try to coerce the insert values to the types
 *                              indicated by the insert attributes
 *   return:
 *   parser(in): handle to context used to parse the insert/merge statement
 *   stmt(in): the AST form of an insert/merge statement
 */
static PT_NODE *
pt_coerce_insert_values (PARSER_CONTEXT * parser, PT_NODE * stmt)
{
  PT_NODE *v = NULL, *a = NULL, *crt_list = NULL;
  int a_cnt = 0, v_cnt = 0;
  PT_NODE *prev = NULL;
  PT_NODE *attr_list = NULL;
  PT_NODE *value_clauses = NULL;

  /* preconditions are not met */
  if (stmt->node_type != PT_INSERT && stmt->node_type != PT_MERGE)
    {
      return NULL;
    }

  if (stmt->node_type == PT_INSERT)
    {
      if (stmt->info.insert.spec && stmt->info.insert.spec->info.spec.remote_server_name)
    {
      assert (stmt->info.insert.spec->info.spec.remote_server_name->node_type == PT_DBLINK_TABLE_DML);
      return stmt;
    }
    }
  else if (stmt->node_type == PT_MERGE)
    {
      if (stmt->info.merge.into && stmt->info.merge.into->info.spec.remote_server_name)
    {
      assert (stmt->info.merge.into->info.spec.remote_server_name->node_type == PT_DBLINK_TABLE_DML);
      return stmt;
    }
    }

  if (stmt->node_type == PT_INSERT)
    {
      attr_list = stmt->info.insert.attr_list;
      value_clauses = stmt->info.insert.value_clauses;
    }
  else
    {
      attr_list = stmt->info.merge.insert.attr_list;
      value_clauses = stmt->info.merge.insert.value_clauses;
    }

  a_cnt = pt_length_of_list (attr_list);

  for (crt_list = value_clauses; crt_list != NULL; crt_list = crt_list->next)
    {
      if (crt_list->info.node_list.list_type == PT_IS_DEFAULT_VALUE)
    {
      v = NULL;
    }
      else if (crt_list->info.node_list.list_type == PT_IS_SUBQUERY)
    {
      /* this sort of nods at union queries */
      v = pt_get_select_list (parser, crt_list->info.node_list.list);
      v_cnt = pt_length_of_select_list (v, EXCLUDE_HIDDEN_COLUMNS);
      if (a_cnt != v_cnt)
        {
          PT_ERRORmf2 (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ATT_CNT_COL_CNT_NE, a_cnt, v_cnt);
        }
      else
        {
          continue;
        }
    }
      else
    {
      v = crt_list->info.node_list.list;
      v_cnt = pt_length_of_list (v);
      if (a_cnt != v_cnt)
        {
          PT_ERRORmf2 (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ATT_CNT_VAL_CNT_NE, a_cnt, v_cnt);
        }
    }
      prev = NULL;
      for (a = attr_list; v != NULL && a != NULL; prev = v, v = v->next, a = a->next)
    {
      /* test assignment compatibility. This sets parser->error_msgs */
      PT_NODE *new_node;

      if (parser->flag.is_parsing_static_sql == 1 && a->node_type != PT_NAME)
        {
          assert (a->node_type == PT_HOST_VAR);
          PT_ERRORmf2 (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_HAS_NO_ATTR,
               pt_short_print (parser, stmt->info.insert.spec->info.spec.entity_name),
               a->info.host_var.label);
          continue;
        }

      new_node = pt_assignment_compatible (parser, a, v);
      if (new_node == NULL)
        {
          /* this in an error and the message was set by the call to pt_assignment_compatible. Just continue */
          continue;
        }

      if (new_node != v)
        {
          v = new_node;
          if (prev == NULL)
        {
          /* first node in the list */
          crt_list->info.node_list.list = v;
        }
          else
        {
          /* relink list to the wrapped node */
          prev->next = v;
        }
        }

      if (v->node_type == PT_HOST_VAR && v->type_enum == PT_TYPE_MAYBE && v->expected_domain == NULL
          && (PT_IS_NUMERIC_TYPE (a->type_enum) || PT_IS_STRING_TYPE (a->type_enum)))
        {
          TP_DOMAIN *d;

          d = pt_node_to_db_domain (parser, a, NULL);
          d = tp_domain_cache (d);
          v->expected_domain = d;
        }
    }
    }
  return stmt;
}

/*
 * pt_check_sub_insert () - Checks if sub-inserts are semantically correct
 *
 * return         : Unchanged node argument.
 * parser (in)        : Parser context.
 * node (in)          : Parse tree node.
 * void_arg (in)      : Unused argument.
 * continue_walk (in) : Continue walk.
 */
static PT_NODE *
pt_check_sub_insert (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  PT_NODE *entity_name = NULL, *value_clauses = NULL;

  if (*continue_walk == PT_STOP_WALK)
    {
      return node;
    }

  switch (node->node_type)
    {
    case PT_INSERT:
      /* continue to checks */
      *continue_walk = PT_LIST_WALK;
      break;
    case PT_SELECT:
    case PT_INTERSECTION:
    case PT_DIFFERENCE:
    case PT_UNION:
      /* stop advancing into this node */
      *continue_walk = PT_LIST_WALK;
      return node;
    default:
      /* do nothing */
      *continue_walk = PT_CONTINUE_WALK;
      return node;
    }
  /* Check current insert node */
  value_clauses = node->info.insert.value_clauses;
  if (value_clauses->next)
    {
      /* Only one row is allowed for sub-inserts */
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DO_INSERT_TOO_MANY, 0);
      if (!pt_has_error (parser))
    {
      PT_ERRORc (parser, node, db_error_string (3));
    }
      *continue_walk = PT_STOP_WALK;
      return node;
    }
  entity_name = node->info.insert.spec->info.spec.entity_name;
  if (entity_name == NULL || entity_name->info.name.db_object == NULL)
    {
      PT_INTERNAL_ERROR (parser, "Unresolved insert spec");
      *continue_walk = PT_STOP_WALK;
      return node;
    }
  if (sm_is_reuse_oid_class (entity_name->info.name.db_object))
    {
      /* Inserting Reusable OID is not allowed */
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NON_REFERABLE_VIOLATION,
          entity_name->info.name.original);
      *continue_walk = PT_STOP_WALK;
      return node;
    }

  /* check sub-inserts for this sub-insert */
  if (value_clauses->info.node_list.list_type != PT_IS_SUBQUERY)
    {
      (void) parser_walk_tree (parser, value_clauses, pt_check_sub_insert, NULL, NULL, NULL);
      if (pt_has_error (parser))
    {
      *continue_walk = PT_STOP_WALK;
    }
    }
  return node;
}

/*
 * pt_count_input_markers () - If the node is a input host variable marker,
 *      compare its index+1 against *num_ptr and record the bigger of
 *      the two into *num_ptr
 *   return:
 *   parser(in):
 *   node(in): the node to check
 *   arg(in/out):
 *   continue_walk(in):
 */

PT_NODE *
pt_count_input_markers (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  int *num_markers;

  num_markers = (int *) arg;

  if (pt_is_input_hostvar (node))
    {
      if (*num_markers < node->info.host_var.index + 1)
    {
      *num_markers = node->info.host_var.index + 1;
    }
    }

  return node;
}

/*
 * pt_count_output_markers () - If the node is a output host variable marker,
 *      compare its index+1 against *num_ptr and record the bigger of
 *      the two into *num_ptr
 *   return:
 *   parser(in):
 *   node(in): the node to check
 *   arg(in/out):
 *   continue_walk(in):
 */
PT_NODE *
pt_count_output_markers (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  int *num_markers;

  num_markers = (int *) arg;

  if (pt_is_output_hostvar (node))
    {
      if (*num_markers < node->info.host_var.index + 1)
    {
      *num_markers = node->info.host_var.index + 1;
    }
    }

  return node;
}

/*
 * pt_has_using_index_clause () -
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in):
 */
PT_NODE *
pt_has_using_index_clause (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  bool *found = (bool *) arg;
  PT_NODE *ui_node;

  switch (node->node_type)
    {
    case PT_DELETE:
      ui_node = node->info.delete_.using_index;
      break;

    case PT_UPDATE:
      ui_node = node->info.update.using_index;
      break;

    case PT_SELECT:
      ui_node = node->info.query.q.select.using_index;
      break;

    default:
      ui_node = NULL;
      break;
    }

  if (ui_node)
    {
      *found = true;
      *continue_walk = PT_STOP_WALK;
    }

  return node;
}

/*
 * pt_validate_query_spec () - check if a query_spec is compatible with a
 *                 given {vclass} object
 *   return: an error code if checking found an error, NO_ERROR otherwise
 *   parser(in): handle to context used to parse the query_specs
 *   s(in): a query_spec in parse_tree form
 *   c(in): a vclass object
 */

int
pt_validate_query_spec (PARSER_CONTEXT * parser, PT_NODE * s, DB_OBJECT * c)
{
  PT_NODE *attrs = NULL;
  int error_code = NO_ERROR;
  bool do_not_replace_orderby;

  assert (parser != NULL && s != NULL && c != NULL);

  /* a syntax error for query_spec */
  if (pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_SYNTAX);
      error_code = er_errid ();
      goto error_exit;
    }

  if (db_is_vclass (c) <= 0)
    {
      error_code = ER_OBJ_INVALID_ARGUMENTS;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
      goto error_exit;
    }

  do_not_replace_orderby = true;
  s = parser_walk_tree (parser, s, pt_set_is_view_spec, (void *) &do_not_replace_orderby, NULL, NULL);
  assert (s != NULL);

  attrs = pt_get_attributes (parser, c);

  /* apply semantic checks to query_spec */
  s = pt_check_vclass_query_spec (parser, s, attrs, db_get_class_name (c), true);
  if (pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_SEMANTIC);
      error_code = er_errid ();
      goto error_exit;
    }
  if (s == NULL)
    {
      error_code = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
      goto error_exit;
    }

  s = pt_type_cast_vclass_query_spec (parser, s, attrs);
  if (pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_SEMANTIC);
      error_code = er_errid ();
      goto error_exit;
    }
  if (s == NULL)
    {
      error_code = ER_GENERIC_ERROR;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error_code, 0);
      goto error_exit;
    }

  return error_code;

error_exit:
  return error_code;
}


/*
 * pt_check_xaction_list () - Checks to see if there is more than one
 *      isolation level clause or more than one timeout value clause
 *   return:
 *   parser(in):
 *   node(in): the node to check
 */

static void
pt_check_xaction_list (PARSER_CONTEXT * parser, PT_NODE * node)
{
  int num_iso_nodes = 0;
  int num_time_nodes = 0;

  (void) parser_walk_tree (parser, node, pt_count_iso_nodes, &num_iso_nodes, NULL, NULL);

  (void) parser_walk_tree (parser, node, pt_count_time_nodes, &num_time_nodes, NULL, NULL);

  if (num_iso_nodes > 1)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_GT_1_ISOLATION_LVL);
    }

  if (num_time_nodes > 1)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_GT_1_TIMEOUT_CLAUSES);
    }
}

/*
 * pt_count_iso_nodes () - returns node unchanged, count by reference
 *   return:
 *   parser(in):
 *   node(in): the node to check
 *   arg(in/out): count of isolation level nodes
 *   continue_walk(in):
 */

static PT_NODE *
pt_count_iso_nodes (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  int *cnt = (int *) arg;

  if (node->node_type == PT_ISOLATION_LVL)
    {
      (*cnt)++;
    }

  return node;
}

/*
 * pt_count_time_nodes () - returns node timeouted, count by reference
 *   return:
 *   parser(in):
 *   node(in): the node to check
 *   arg(in/out): count of timeout nodes
 *   continue_walk(in):
 */
static PT_NODE *
pt_count_time_nodes (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  int *cnt = (int *) arg;

  if (node->node_type == PT_TIMEOUT)
    {
      (*cnt)++;
    }

  return node;
}

/*
 * pt_check_isolation_lvl () - checks isolation level node
 *   return:
 *   parser(in):
 *   node(in/out): the node to check
 *   arg(in):
 *   continue_walk(in):
 *
 * Note :
 * checks
 * 1) if isolation levels for schema & instances are compatible.
 * 2) if isolation number entered, check to see if it is valid.
 */

static PT_NODE *
pt_check_isolation_lvl (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  DB_TRAN_ISOLATION cur_lvl;
  int dummy;

  if (node->node_type == PT_ISOLATION_LVL)
    {
      if (node->info.isolation_lvl.level != NULL)
    {
      /* assume correct type, value checking will be done at run-time */
      return node;
    }

      /* check to make sure an isolation level has been given */
      if ((node->info.isolation_lvl.schema == PT_NO_ISOLATION_LEVEL)
      && (node->info.isolation_lvl.instances == PT_NO_ISOLATION_LEVEL)
      && (node->info.isolation_lvl.async_ws == false))
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NO_ISOLATION_LVL_MSG);
    }

      /* get the current isolation level in case user is defaulting either the schema or the instances level. */
      (void) db_get_tran_settings (&dummy, &cur_lvl);

      if (node->info.isolation_lvl.schema == PT_NO_ISOLATION_LEVEL)
    {
      switch (cur_lvl)
        {
        case TRAN_UNKNOWN_ISOLATION:
          /* in this case, the user is specifying only the instance level when there was not a previous isolation
           * level set.  Default the schema isolation level to the instance isolation level. */
          node->info.isolation_lvl.schema = node->info.isolation_lvl.instances;
          break;

        case TRAN_READ_COMMITTED:
        case TRAN_REPEATABLE_READ:
          node->info.isolation_lvl.schema = PT_REPEATABLE_READ;
          break;

        case TRAN_SERIALIZABLE:
          node->info.isolation_lvl.schema = PT_SERIALIZABLE;
          break;
        }
    }

      if (node->info.isolation_lvl.instances == PT_NO_ISOLATION_LEVEL)
    {
      switch (cur_lvl)
        {
        case TRAN_UNKNOWN_ISOLATION:
          /* in this case, the user is specifying only the schema level when there was not a previous isolation
           * level set.  Default the instances isolation level to the schema isolation level. */
          node->info.isolation_lvl.instances = node->info.isolation_lvl.schema;
          break;

        case TRAN_READ_COMMITTED:
          node->info.isolation_lvl.instances = PT_READ_COMMITTED;
          break;

        case TRAN_REPEATABLE_READ:
          node->info.isolation_lvl.instances = PT_REPEATABLE_READ;
          break;

        case TRAN_SERIALIZABLE:
          node->info.isolation_lvl.instances = PT_SERIALIZABLE;
          break;
        }
    }

      /* coercing/correcting of incompatible level happens in do_set_xaction() */
    }

  return node;
}

#if defined (ENABLE_UNUSED_FUNCTION)
/*
 * pt_find_attr_def () - Finds the PT_NODE in attr_def_list with the same
 *               original_name as the given name
 *   return:  db_user instance if user exists, NULL otherwise.
 *   attr_def_list(in): the list of attr_def's in a CREATE_ENTITY node
 *   name(in): a PT_NAME node
 */

PT_NODE *
pt_find_attr_def (const PT_NODE * attr_def_list, const PT_NODE * name)
{
  PT_NODE *p;

  for (p = (PT_NODE *) attr_def_list; p; p = p->next)
    {
      if (intl_identifier_casecmp (p->info.attr_def.attr_name->info.name.original, name->info.name.original) == 0)
    {
      break;
    }
    }

  return p;
}

/*
 * pt_find_cnstr_def () - Finds the PT_NODE in cnstr_def_list with the same
 *                        original_name as the given name
 *   return:  attribute instance iff attribute exists, NULL otherwise.
 *   cnstr_def_list(in): the list of constraint elements
 *   name(in): a PT_NAME node
 */
PT_NODE *
pt_find_cnstr_def (const PT_NODE * cnstr_def_list, const PT_NODE * name)
{
  PT_NODE *p;

  for (p = (PT_NODE *) cnstr_def_list; p; p = p->next)
    {
      if (intl_identifier_casecmp (p->info.name.original, name->info.name.original) == 0)
    {
      break;
    }
    }

  return p;
}
#endif

/*
 * pt_check_constraint () - Checks the given constraint appears to be valid
 *   return:  the constraint node
 *   parser(in): the current parser context
 *   create(in): a CREATE_ENTITY node
 *   constraint(in): a CONSTRAINT node, assumed to have come from the
 *                   constraint_list of "create"
 *
 * Note :
 *    Right now single-column UNIQUE and NOT NULL constraints are the only
 *    ones that are understood.  All others are ignored (with a warning).
 *    Unfortunately, this can't do the whole job because at this point we know
 *    nothing about inherited attributes, etc.  For example, someone could be
 *    trying to add a UNIQUE constraint to an inherited attribute, but we won't
 *    be able to handle it because we'll be unable to resolve the name.  Under
 *    the current architecture the template stuff will need to be extended to
 *    understand constraints.
 */

static PT_NODE *
pt_check_constraint (PARSER_CONTEXT * parser, const PT_NODE * create, const PT_NODE * constraint)
{
  switch (constraint->info.constraint.type)
    {
    case PT_CONSTRAIN_UNKNOWN:
      goto warning;

    case PT_CONSTRAIN_NULL:
    case PT_CONSTRAIN_NOT_NULL:
    case PT_CONSTRAIN_UNIQUE:
      break;

    case PT_CONSTRAIN_PRIMARY_KEY:
    case PT_CONSTRAIN_FOREIGN_KEY:
    case PT_CONSTRAIN_CHECK:
      if (create->info.create_entity.entity_type != PT_CLASS)
    {
      goto error;
    }
      else
    {
      goto warning;
    }
    }

  return (PT_NODE *) constraint;

warning:
  PT_WARNINGmf (parser, constraint, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UNIMPLEMENTED_CONSTRAINT,
        parser_print_tree (parser, constraint));
  return (PT_NODE *) constraint;

error:
  PT_ERRORm (parser, constraint, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_WANT_NO_CONSTRAINTS);
  return NULL;
}

/*
 * pt_check_constraints () - Checks all of the constraints given in
 *                           this CREATE_ENTITY node
 *   return:  the CREATE_ENTITY node
 *   parser(in): the current parser context
 *   create(in): a CREATE_ENTITY node
 */
static PT_NODE *
pt_check_constraints (PARSER_CONTEXT * parser, const PT_NODE * create)
{
  PT_NODE *constraint;

  for (constraint = create->info.create_entity.constraint_list; constraint; constraint = constraint->next)
    {
      (void) pt_check_constraint (parser, create, constraint);
    }

  return (PT_NODE *) create;
}

/*
 * pt_find_class_of_index () - Find the name of the class that has a given
 *                             index (specified by its name and type)
 *   return: a PT_NAME node with the class name or NULL on error
 *   parser(in):
 *   index_name(in):
 *   index_type(in):
 *
 * Note:
 *   Only constraint types that satisfy the DB_IS_CONSTRAINT_INDEX_FAMILY
 *   condition will be searched for.
 */
PT_NODE *
pt_find_class_of_index (PARSER_CONTEXT * parser, const char *const index_name, const DB_CONSTRAINT_TYPE index_type)
{
  PT_NODE *node = NULL;
  DB_OBJECT *const class_ = db_find_class_of_index (index_name, index_type);

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

/*
 * pt_check_defaultf () - Checks to see if default function is well-formed
 *   return: none
 *   parser(in):
 *   node(in): the node to check
 */
static int
pt_check_defaultf (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *arg;

  assert (node != NULL && node->node_type == PT_EXPR && node->info.expr.op == PT_DEFAULTF);
  if (node == NULL || node->node_type != PT_EXPR || node->info.expr.op != PT_DEFAULTF)
    {
      PT_INTERNAL_ERROR (parser, "bad node type");
      return ER_FAILED;
    }

  arg = node->info.expr.arg1;

  /* OIDs don't have default value */
  if (arg == NULL || arg->node_type != PT_NAME || arg->info.name.meta_class == PT_OID_ATTR
      || !PT_NAME_INFO_IS_FLAGED (arg, PT_NAME_DEFAULTF_ACCEPTS))
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DEFAULT_JUST_COLUMN_NAME);
      return ER_FAILED;
    }

  /* Argument of DEFAULT function should be given. So, PT_NAME_INFO_FILL_DEFAULT bit might be always set when
   * expression node was created. The following assertion and defensive code will be used to handle unexpected
   * situation. */
  assert (PT_NAME_INFO_IS_FLAGED (arg, PT_NAME_INFO_FILL_DEFAULT) && arg->info.name.default_value != NULL);
  if (!PT_NAME_INFO_IS_FLAGED (arg, PT_NAME_INFO_FILL_DEFAULT) || arg->info.name.default_value == NULL)
    {
      PT_INTERNAL_ERROR (parser, "bad DEFAULTF node");
      return ER_FAILED;
    }

  /* In case of no default value defined on an attribute: DEFAULT function returns NULL when the attribute given as
   * argument has UNIQUE or no constraint, but it returns a semantic error for PRIMARY KEY or NOT NULL constraint. This
   * function does not return a semantic error for attributes with auto_increment because, regardless of the default
   * value, NULL will not be inserted there. */
  if (arg->info.name.resolved && arg->info.name.original)
    {
      DB_ATTRIBUTE *db_att = NULL;
      db_att = db_get_attribute_by_name (arg->info.name.resolved, arg->info.name.original);

      if (db_att && !db_attribute_is_auto_increment (db_att))
    {
      if ((db_attribute_is_primary_key (db_att) || db_attribute_is_non_null (db_att))
          && arg->info.name.default_value->node_type == PT_VALUE
          && DB_IS_NULL (&(arg->info.name.default_value->info.value.db_value)))
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_ATTRIBUTE_CANT_BE_NULL, 1, arg->info.name.original);
          PT_ERRORc (parser, arg, er_msg ());
          return ER_FAILED;
        }
    }
    }
  return NO_ERROR;
}


/*
 * pt_check_auto_increment_table_option () - Checks that the AUTO_INCREMENT
 *    table option has a non-ambiguous field to apply to and that
 *    the respective field does not have an explicit auto_increment start
 *    value.
 *
 *   NOTE:  the function also modifies the parse tree by rewriting the
 *      table option as an AUTO_INCREMENT constraint for the respective field.
 *
 *   return: error code
 *   parser(in): the current parser context
 *   create(in): a CREATE_ENTITY node
 */
static int
pt_check_auto_increment_table_option (PARSER_CONTEXT * parser, PT_NODE * create)
{
  PT_NODE *attr = NULL;
  PT_NODE *auto_inc_attr = NULL;
  PT_NODE *start_val = NULL;
  PT_NODE *increment_val = NULL;
  PT_NODE *tbl_opt = NULL;
  PT_NODE *prev_tbl_opt = NULL;

  /* do we have EXACTLY ONE auto_increment node in our attr list? */
  for (attr = create->info.create_entity.attr_def_list; attr != NULL; attr = attr->next)
    {
      if (attr->info.attr_def.auto_increment != NULL)
    {
      if (auto_inc_attr != NULL)
        {
          /* we already found an auto increment attr! */
          PT_ERRORm (parser, create, MSGCAT_SET_ERROR, -(ER_AUTO_INCREMENT_SINGLE_COL_AMBIGUITY));
          return ER_FAILED;
        }
      auto_inc_attr = attr;
    }
    }

  if (auto_inc_attr == NULL)
    {
      PT_ERRORm (parser, create, MSGCAT_SET_ERROR, -(ER_AUTO_INCREMENT_SINGLE_COL_AMBIGUITY));
      return ER_FAILED;
    }

  /* here we have exactly ONE attribute with auto_increment */

  if (auto_inc_attr->info.attr_def.auto_increment->info.auto_increment.start_val != NULL)
    {
      /* does it already have a start value? this is not good */
      PT_ERRORm (parser, create, MSGCAT_SET_ERROR, -(ER_AUTO_INCREMENT_SINGLE_COL_AMBIGUITY));
      return ER_FAILED;
    }

  /* alter the tree */

  /* locate the tbl opt, save its numeric value and destroy it */

  prev_tbl_opt = NULL;
  tbl_opt = create->info.create_entity.table_option_list;
  while (tbl_opt != NULL)
    {
      if (tbl_opt->info.table_option.option == PT_TABLE_OPTION_AUTO_INCREMENT)
    {
      start_val = parser_copy_tree (parser, tbl_opt->info.table_option.val);
      if (prev_tbl_opt != NULL)
        {
          prev_tbl_opt->next = tbl_opt->next;
        }
      else
        {
          create->info.create_entity.table_option_list = tbl_opt->next;
        }

      tbl_opt->next = NULL;
      parser_free_tree (parser, tbl_opt);
      break;

    }

      prev_tbl_opt = tbl_opt;
      tbl_opt = tbl_opt->next;
    }

  /* add the two nodes to the attribute definition */
  increment_val = pt_make_integer_value (parser, 1);

  if (start_val == NULL || increment_val == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NO_ERROR;
    }

  auto_inc_attr->info.attr_def.auto_increment->info.auto_increment.start_val = start_val;
  auto_inc_attr->info.attr_def.auto_increment->info.auto_increment.increment_val = increment_val;

  return NO_ERROR;
}

/*
 * pt_check_group_concat_order_by () - checks an ORDER_BY clause of a
 *                GROUP_CONCAT aggregate function;
 *                if the expression or identifier from
 *                ORDER BY clause matches an argument of function,
 *                the ORDER BY item is converted into associated
 *                number.
 *   return:
 *   parser(in):
 *   query(in): query node has ORDER BY
 *
 *
 *  Note :
 *
 * Only one order by item is supported :
 *    - if it is an INTEGER, make sure it does not exceed the number of items
 *  in the argument list.
 *    - if it is a path expression, match it with an argument in the
 *  function's argument list and replace the node with a PT_VALUE
 *  with corresponding number.
 *    - if it doesn't match, an error is issued.
 */
static int
pt_check_group_concat_order_by (PARSER_CONTEXT * parser, PT_NODE * func)
{
  PT_NODE *arg_list = NULL;
  PT_NODE *order_by = NULL;
  PT_NODE *arg = NULL;
  PT_NODE *temp, *order;
  int n, i, arg_list_len;
  int error = NO_ERROR;
  PT_NODE *group_concat_sep_node_save = NULL;

  assert (func->info.function.function_type == PT_GROUP_CONCAT);

  /* get ORDER BY clause */
  order_by = func->info.function.order_by;
  if (order_by == NULL)
    {
      goto error_exit;
    }

  arg_list = func->info.function.arg_list;
  if (arg_list == NULL)
    {
      PT_ERRORmf (parser, func, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_INTERNAL_FUNCTION, "GROUP_CONCAT");
      goto error_exit;
    }

  /* remove separator from list of arguments */
  group_concat_sep_node_save = func->info.function.arg_list->next;
  func->info.function.arg_list->next = NULL;

  /* save original length of select_list */
  arg_list_len = pt_length_of_list (arg_list);
  for (order = order_by; order != NULL; order = order->next)
    {
      /* get the EXPR */
      PT_NODE *r = order->info.sort_spec.expr;
      if (r == NULL)
    {           /* impossible case */
      continue;
    }

      if (PT_IS_LOB_TYPE (r->type_enum))
    {
      PT_ERRORmf (parser, r, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NO_ORDERBY_ALLOWED,
              pt_short_print (parser, r));
      goto error_exit;
    }

      /* if a good integer, done */
      if (r->node_type == PT_VALUE)
    {
      if (r->type_enum == PT_TYPE_INTEGER)
        {
          n = r->info.value.data_value.i;
          /* check size of the integer */
          if (n > arg_list_len || n < 1)
        {
          error = MSGCAT_SEMANTIC_SORT_SPEC_RANGE_ERR;
          PT_ERRORmf (parser, r, MSGCAT_SET_PARSER_SEMANTIC, error, n);
          /* go ahead */
        }
          else
        {
          /* goto associated argument: */
          for (arg = arg_list, i = 1; i < n; i++)
            {
              arg = arg->next;
            }
        }
        }
      else
        {
          error = MSGCAT_SEMANTIC_SORT_SPEC_WANT_NUM;
          PT_ERRORm (parser, r, MSGCAT_SET_PARSER_SEMANTIC, error);
          /* go ahead */
        }
    }
      else
    {
      char *r_str = NULL;
      /* not an integer value. Try to match with something in the select list. */

      n = 1;        /* a counter for position in select_list */
      if (r->node_type != PT_NAME && r->node_type != PT_DOT_)
        {
          r_str = parser_print_tree (parser, r);
        }

      for (arg = arg_list; arg != NULL; arg = arg->next)
        {
          /* if match, break; */
          if (r->node_type == arg->node_type)
        {
          if (r->node_type == PT_NAME || r->node_type == PT_DOT_)
            {
              if (pt_check_path_eq (parser, r, arg) == 0)
            {
              break;    /* match */
            }
            }
          else
            {
              if (pt_str_compare (r_str, parser_print_tree (parser, arg), CASE_INSENSITIVE) == 0)
            {
              break;    /* match */
            }
            }
        }
          n++;
        }

      /* if end of list -> error : currently aggregate functions don't support other order by expression than
       * arguments */
      if (arg == NULL)
        {
          error = MSGCAT_SEMANTIC_GROUP_CONCAT_ORDERBY_SAME_EXPR;
          PT_ERRORm (parser, r, MSGCAT_SET_PARSER_SEMANTIC, error);
          /* go ahead */
        }
      else
        {
          /* we got a match=n, Create a value node and replace expr with it */
          temp = parser_new_node (parser, PT_VALUE);
          if (temp == NULL)
        {
          error = MSGCAT_SEMANTIC_OUT_OF_MEMORY;
          PT_ERRORm (parser, r, MSGCAT_SET_PARSER_SEMANTIC, error);
        }
          else
        {
          temp->type_enum = PT_TYPE_INTEGER;
          temp->info.value.data_value.i = n;
          pt_value_to_db (parser, temp);
          parser_free_tree (parser, r);
          order->info.sort_spec.expr = temp;
        }
        }
    }

      if (error != NO_ERROR)
    {           /* something wrong, exit */
      goto error_exit;
    }

      /* at this point <n> contains the sorting position : either specified in statement or computed, and <arg> is the
       * corresponding function argument */
      assert (arg != NULL);
      assert (n > 0 && n <= arg_list_len);

      /* set order by position num */
      order->info.sort_spec.pos_descr.pos_no = n;

      /* set order_by domain info */
      if (arg->type_enum != PT_TYPE_NONE && arg->type_enum != PT_TYPE_MAYBE)
    {           /* is resolved */
      order->info.sort_spec.pos_descr.dom = pt_xasl_node_to_domain (parser, arg);
    }
    }

  assert (func->info.function.order_by->next == NULL);

error_exit:
  if (group_concat_sep_node_save != NULL)
    {
      func->info.function.arg_list->next = group_concat_sep_node_save;
    }

  return error;
}

/*
 * pt_check_cume_dist_percent_rank_order_by () - checks an ORDER_BY clause of a
 *                CUME_DIST aggregate function;
 *                if the expression or identifier from
 *                ORDER BY clause matches an argument of function,
 *                the ORDER BY item is converted into associated
 *                number.
 *   return: NO_ERROR or error_code
 *   parser(in):
 *   func(in):
 *
 *
 *  Note :
 *    We need to check arguments and order by,
 *    because the arguments must be constant expression and
 *    match the ORDER BY clause by position.
 */

static int
pt_check_cume_dist_percent_rank_order_by (PARSER_CONTEXT * parser, PT_NODE * func)
{
  PT_NODE *arg_list = NULL;
  PT_NODE *order_by = NULL;
  PT_NODE *arg = NULL;
  PT_NODE *order = NULL;
  PT_NODE *order_expr = NULL;
  DB_OBJECT *obj = NULL;
  DB_ATTRIBUTE *att = NULL;
  TP_DOMAIN *dom = NULL;
  DB_VALUE *value = NULL;
  int i, arg_list_len, order_by_list_len;
  int error = NO_ERROR;
  const char *func_name;

  /* get function name for ERROR message */
  if (func->info.function.function_type == PT_CUME_DIST)
    {
      func_name = "CUME_DIST";
    }
  else if (func->info.function.function_type == PT_PERCENT_RANK)
    {
      func_name = "PERCENT_RANK";
    }
  else
    {
      assert (false);
      return ER_FAILED;
    }

  /* first check if the arguments are constant */
  arg_list = func->info.function.arg_list;
  order_by = func->info.function.order_by;

  /* for analytic function */
  if (func->info.function.analytic.is_analytic)
    {
      if (arg_list != NULL || order_by != NULL)
    {
      error = ER_FAILED;
      PT_ERRORmf (parser, func, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_INTERNAL_FUNCTION, func_name);
    }
      goto error_exit;
    }

  /* aggregate function */
  if (arg_list == NULL || order_by == NULL)
    {
      error = ER_FAILED;
      PT_ERRORmf (parser, func, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_INTERNAL_FUNCTION, func_name);
      goto error_exit;
    }

  /* save original length of select_list */
  arg_list_len = pt_length_of_list (arg_list);
  order_by_list_len = pt_length_of_list (order_by);
  if (arg_list_len != order_by_list_len)
    {
      PT_ERRORmf (parser, func, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UNMACHTED_ARG_ORDER, func_name);
      goto error_exit;
    }

  arg = arg_list;
  order = order_by;
  for (i = 0; i < arg_list_len; i++)
    {
      /* check argument type: arguments must be constant */
      if (!pt_is_const_expr_node (arg))
    {
      PT_ERRORmf (parser, func, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_CONSTANT_PARAMETER, func_name);
      goto error_exit;
    }

      /* check order by */
      order_expr = order->info.sort_spec.expr;
      if (order->node_type != PT_SORT_SPEC || (order_expr->node_type != PT_NAME && order_expr->node_type != PT_VALUE)
      || PT_IS_LOB_TYPE (order_expr->type_enum))
    {
      PT_ERRORmf (parser, func, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_FUNCTION_ORDERBY, func_name);
      goto error_exit;
    }

      if (order_expr->node_type == PT_NAME)
    {
      /* check if the arg matches order by clause by position */
      dom = NULL;
      obj = db_find_class (order_expr->info.name.resolved);
      if (obj != NULL)
        {
          att = db_get_attribute (obj, order_expr->info.name.original);
          if (att != NULL)
        {
          dom = att->domain;
        }
        }

      /* Note: it is possible that class is not found. i.e. 'select PERCENT_RANK(null,3) within group (order by
       * score,score1) from (select NULL score,'00001' score1 from db_root) S;' We just let it go if an attribute
       * could not be found. */

      /* for common values */
      if (arg->node_type == PT_VALUE && dom != NULL)
        {
          value = &arg->info.value.db_value;
          error = db_value_coerce (value, value, dom);
          if (error != NO_ERROR)
        {
          PT_ERRORmf2 (parser, func, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CANT_COERCE_TO,
                   pt_short_print (parser, arg), pt_show_type_enum (order_expr->type_enum));
          goto error_exit;
        }
        }
    }

      /* to next */
      order->info.sort_spec.pos_descr.pos_no = i + 1;
      arg = arg->next;
      order = order->next;
    }

error_exit:

  return error;
}


/*
 * pt_has_parameters () - check if a statement uses session variables
 * return   : true if the statement uses session variables
 * parser (in)  : parser context
 * stmt (in)    : statement
 */
static bool
pt_has_parameters (PARSER_CONTEXT * parser, PT_NODE * stmt)
{
  bool has_paramenters = false;

  parser_walk_tree (parser, stmt, pt_is_parameter_node, &has_paramenters, NULL, NULL);

  return has_paramenters;
}

/*
 * pt_is_parameter_node () - check if a node is a session variable
 * return : node
 * parser (in) : parser context
 * node (in)   : node
 * arg (in)    :
 * continue_walk (in) :
 */
static PT_NODE *
pt_is_parameter_node (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  bool *is_parameter = (bool *) arg;
  *continue_walk = PT_CONTINUE_WALK;

  if (*is_parameter)
    {
      /* stop checking, there already is a parameter in the statement */
      return node;
    }

  if (node->node_type == PT_EXPR)
    {
      if (node->info.expr.op == PT_EVALUATE_VARIABLE || node->info.expr.op == PT_DEFINE_VARIABLE)
    {
      *is_parameter = true;
      *continue_walk = PT_STOP_WALK;
    }
    }

  return node;
}

/*
 * pt_resolve_sort_spec_expr - resolves a sort spec expression
 *  returns: parser node or NULL on error
 *  sort_spec(in): PT_SORT_SPEC node whose expression must be resolved
 *  select_list(in): statement's select list for PT_VALUE lookup
 */
static PT_NODE *
pt_resolve_sort_spec_expr (PARSER_CONTEXT * parser, PT_NODE * sort_spec, PT_NODE * select_list)
{
  PT_NODE *expr, *resolved;

  if (parser == NULL || sort_spec == NULL)
    {
      /* nothing to do */
      return NULL;
    }

  if (sort_spec->node_type != PT_SORT_SPEC)
    {
      PT_INTERNAL_ERROR (parser, "expecting a sort spec");
      return NULL;
    }

  expr = sort_spec->info.sort_spec.expr;
  if (expr == NULL)
    {
      PT_INTERNAL_ERROR (parser, "null sort expression");
      return NULL;
    }

  if (expr->node_type == PT_EXPR && expr->info.expr.op == PT_UNARY_MINUS)
    {
      PT_ERRORm (parser, sort_spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SORT_SPEC_WANT_NUM);
      return NULL;
    }
  else if (expr->node_type != PT_VALUE)
    {
      return expr;
    }

  /* we have a PT_VALUE sort expression; look it up in select list */
  if (expr->type_enum == PT_TYPE_INTEGER)
    {
      int index = expr->info.value.data_value.i;
      resolved = pt_get_node_from_list (select_list, index - 1);

      if (resolved != NULL)
    {
      return resolved;
    }
      else
    {
      PT_ERRORmf (parser, sort_spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SORT_SPEC_RANGE_ERR, index);
      return NULL;
    }
    }
  else
    {
      PT_ERRORm (parser, sort_spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SORT_SPEC_WANT_NUM);
      return NULL;
    }
}

/*
 * pt_compare_sort_spec_expr - compare two sort expressions
 *  returns: true if expressions are the same, false otherwise
 *  expr1(in): first expression
 *  expr2(in): second expression
 */
bool
pt_compare_sort_spec_expr (PARSER_CONTEXT * parser, PT_NODE * expr1, PT_NODE * expr2)
{
  if (parser == NULL || expr1 == NULL || expr2 == NULL || (expr1->node_type != expr2->node_type))
    {
      return false;
    }

  if ((expr1->node_type == PT_NAME || expr1->node_type == PT_DOT_)
      && (expr2->node_type == PT_NAME || expr2->node_type == PT_DOT_))
    {
      /* we have comparable names */
      if (pt_check_path_eq (parser, expr1, expr2) == 0)
    {
      /* name match */
      return true;
    }
    }
  else
    {
      /* brute method, compare printed trees */
      char *str_expr = parser_print_tree (parser, expr1);
      char *str_spec_expr = parser_print_tree (parser, expr2);

      if (pt_str_compare (str_expr, str_spec_expr, CASE_INSENSITIVE) == 0)
    {
      /* match */
      return true;
    }
    }

  /* no match */
  return false;
}

/*
 * pt_find_matching_sort_spec - find a matching sort spec in a spec list
 *  return: match or NULL
 *  parser(in): parser context
 *  spec(in): sort spec to look for
 *  spec_list(in): sort spec list to look into
 *  select_list(in): statement's select list, for PT_VALUE lookup
 */
static PT_NODE *
pt_find_matching_sort_spec (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * spec_list, PT_NODE * select_list)
{
  PT_NODE *spec_expr, *expr;

  if (parser == NULL || spec == NULL || spec_list == NULL)
    {
      /* nothing to do here */
      return NULL;
    }

  /* fetch sort expression */
  spec_expr = pt_resolve_sort_spec_expr (parser, spec, select_list);
  if (spec_expr == NULL)
    {
      return NULL;
    }

  /* iterate list and check for match */
  while (spec_list)
    {
      /* fetch sort expression */
      expr = pt_resolve_sort_spec_expr (parser, spec_list, select_list);
      if (expr == NULL)
    {
      return NULL;
    }

      /* compare */
      if (pt_compare_sort_spec_expr (parser, expr, spec_expr))
    {
      /* found match */
      return spec_list;
    }

      /* advance */
      spec_list = spec_list->next;
    }

  /* nothing was found */
  return NULL;
}

/*
 * pt_remove_unusable_sort_specs () - remove unusable sort specs
 *   return: new list, after filtering sort specs
 *   parser(in): parser context
 *   list(in): spec list
 * Note:
 *  This call remove useless sort specs like NULL and constant expressions
 *  (e.g.: order by null, order by 1 + 5, etc)
 */
static PT_NODE *
pt_remove_unusable_sort_specs (PARSER_CONTEXT * parser, PT_NODE * list)
{
  PT_NODE *item, *expr, *save_next;

  /* check nulls */
  if (parser == NULL)
    {
      assert (false);
      return NULL;
    }

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

  /* remove nulls */
  item = list;
  list = NULL;
  while (item)
    {
      /* unlink */
      save_next = item->next;
      item->next = NULL;

      if (item->node_type != PT_SORT_SPEC || item->info.sort_spec.expr == NULL)
    {
      assert (false);
      PT_INTERNAL_ERROR (parser, "invalid sort spec");
    }

      expr = item->info.sort_spec.expr;
      if (expr->node_type == PT_VALUE && expr->type_enum == PT_TYPE_NULL)
    {
      /* NULL spec, get rid of it */
      parser_free_tree (parser, item);
    }
      else if (expr->node_type == PT_EXPR && pt_is_const_expr_node (expr))
    {
      /* order by constant which is not place holder, get rid of it */
      parser_free_tree (parser, item);
    }
      else
    {
      /* normal spec, keep it */
      list = parser_append_node (item, list);
    }

      /* continue */
      item = save_next;
    }

  /* list contains only normal specs */
  return list;
}

/*
 * pt_check_analytic_function () -
 *   return:
 *   parser(in):
 *   func(in): Function node
 *   arg(in): SELECT node
 *   continue_walk(in):
 *
 */
static PT_NODE *
pt_check_analytic_function (PARSER_CONTEXT * parser, PT_NODE * func, void *arg, int *continue_walk)
{
  PT_NODE *arg_list, *partition_by, *order_by, *select_list;
  PT_NODE *order, *query;
  PT_NODE *link = NULL, *order_list = NULL, *match = NULL;
  PT_NODE *new_order = NULL;

  if (func->node_type != PT_FUNCTION || !func->info.function.analytic.is_analytic)
    {
      return func;
    }

  query = (PT_NODE *) arg;
  if (query->node_type != PT_SELECT)
    {
      PT_ERRORmf (parser, func, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_ALLOWED_HERE,
          pt_short_print (parser, func));
      return func;
    }

  arg_list = func->info.function.arg_list;
  if (arg_list == NULL && func->info.function.function_type != PT_COUNT_STAR
      && func->info.function.function_type != PT_ROW_NUMBER && func->info.function.function_type != PT_RANK
      && func->info.function.function_type != PT_DENSE_RANK && func->info.function.function_type != PT_CUME_DIST
      && func->info.function.function_type != PT_PERCENT_RANK)
    {
      PT_ERRORmf (parser, func, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_FUNCTION_NO_ARGS,
          pt_short_print (parser, func));
      return func;
    }

  /* median doesn't support over(order by ...) */
  if (func->info.function.function_type == PT_MEDIAN)
    {
      if (func->info.function.analytic.order_by != NULL)
    {
      PT_ERRORm (parser, func, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_MEDIAN_FUNC_NOT_ALLOW_ORDER_BY);
      return func;
    }
      else if (!PT_IS_CONST (arg_list))
    {
      /* only sort data when arg is not constant */
      new_order = parser_new_node (parser, PT_SORT_SPEC);
      if (new_order == NULL)
        {
          PT_ERRORm (parser, func, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return func;
        }

      new_order->info.sort_spec.asc_or_desc = PT_ASC;
      new_order->info.sort_spec.nulls_first_or_last = PT_NULLS_DEFAULT;
      new_order->info.sort_spec.expr = parser_copy_tree (parser, arg_list);
      if (new_order->info.sort_spec.expr == NULL)
        {
          PT_ERRORm (parser, func, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return func;
        }

      func->info.function.analytic.order_by = new_order;
    }
    }

  /* remove NULL specs */
  func->info.function.analytic.partition_by =
    pt_remove_unusable_sort_specs (parser, func->info.function.analytic.partition_by);
  func->info.function.analytic.order_by = pt_remove_unusable_sort_specs (parser, func->info.function.analytic.order_by);

  partition_by = func->info.function.analytic.partition_by;
  order_by = func->info.function.analytic.order_by;
  select_list = query->info.query.q.select.list;

  /* order_by sort direction has priority over partition_by direction */
  if (order_by != NULL && partition_by != NULL)
    {
      PT_NODE *part, *match;

      /* iterate partition_by nodes */
      for (part = partition_by; part; part = part->next)
    {
      /* find matching sort spec in order_by list and copy direction */
      match = pt_find_matching_sort_spec (parser, part, order_by, select_list);

      if (match != NULL)
        {
          part->info.sort_spec.asc_or_desc = match->info.sort_spec.asc_or_desc;
          part->info.sort_spec.nulls_first_or_last = match->info.sort_spec.nulls_first_or_last;
        }
    }
    }

  /* link partition and order lists together */
  for (link = partition_by; link && link->next; link = link->next)
    {
      ;
    }
  if (link)
    {
      order_list = partition_by;
      link->next = order_by;
    }
  else
    {
      order_list = order_by;
    }

  /* check for nested analytic functions */
  if (pt_has_analytic (parser, order_list))
    {
      PT_ERRORm (parser, func, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NESTED_ANALYTIC_FUNCTIONS);
      goto error_exit;
    }

  /* replace names/exprs with positions in select list where possible; this also re-processes PT_VALUE sort expressions
   * so we can identify and reduce cases like: SELECT a, b, a, AVG(b) OVER (PARTITION BY A ORDER BY 1 asc, 3 asc) */
  for (order = order_list; order; order = order->next)
    {
      PT_NODE *expr, *col, *temp;
      int index;

      /* resolve sort spec */
      expr = pt_resolve_sort_spec_expr (parser, order, select_list);
      if (expr == NULL)
    {
      goto error_exit;
    }

      /* try to match with something in the select list */
      for (col = select_list, index = 1; col; col = col->next, index++)
    {
      if (col->flag.is_hidden_column)
        {
          /* skip hidden columns; they might disappear later on */
          continue;
        }

      if (pt_compare_sort_spec_expr (parser, expr, col))
        {
          /* found a match in select list */
          break;
        }
    }

      /* if we have a match in the select list, we can replace it on the spot; otherwise, wait for XASL generation */
      if (col != NULL)
    {
      PT_NODE *save_next = col->next;

      col->next = NULL;
      if (pt_has_analytic (parser, col))
        {
          col->next = save_next;

          /* sort expression contains analytic function */
          PT_ERRORm (parser, func, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NESTED_ANALYTIC_FUNCTIONS);
          goto error_exit;
        }
      else
        {
          col->next = save_next;
        }

      /* create a value node and replace expr with it */
      temp = parser_new_node (parser, PT_VALUE);
      if (temp == NULL)
        {
          PT_ERRORm (parser, expr, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          goto error_exit;
        }
      else
        {
          temp->type_enum = PT_TYPE_INTEGER;
          temp->info.value.data_value.i = index;
          (void) pt_value_to_db (parser, temp);
          parser_free_tree (parser, order->info.sort_spec.expr);
          order->info.sort_spec.expr = temp;
        }

      /* set position descriptor and resolve domain */
      order->info.sort_spec.pos_descr.pos_no = index;
      if (col->type_enum != PT_TYPE_NONE && col->type_enum != PT_TYPE_MAYBE)
        {           /* is resolved */
          order->info.sort_spec.pos_descr.dom = pt_xasl_node_to_domain (parser, col);
        }
    }
    }

  /* check for duplicate entries */
  for (order = order_list; order; order = order->next)
    {
      PT_NODE *temp;

      while ((match = pt_find_matching_sort_spec (parser, order, order->next, select_list)))
    {
      if ((order->info.sort_spec.asc_or_desc != match->info.sort_spec.asc_or_desc)
          || (pt_to_null_ordering (order) != pt_to_null_ordering (match)))
        {
          PT_ERRORmf (parser, match, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SORT_DIR_CONFLICT,
              pt_short_print (parser, match));
          goto error_exit;
        }
      else
        {
          /* check if we are going to remove the link */
          if (link && match == link)
        {
          temp = order_list;
          while (temp->next != match)
            {
              temp = temp->next;
            }
          link = temp;
        }

          /* check if we are going to remove the first ORDER BY node */
          if (link && match == link->next)
        {
          /* func may be printed again by pt_find_matching_sort_spec, make sure we don't reference garbage in
           * order_by */
          func->info.function.analytic.order_by = match->next;
        }

          /* remove match */
          order->next = pt_remove_from_list (parser, match, order->next);
        }
    }
    }

error_exit:
  /* un-link partition and order lists */
  if (link)
    {
      func->info.function.analytic.order_by = link->next;
      link->next = NULL;
    }

  return func;
}

/* pt_check_function_index_expr () - check if there is at most one expression
 *                   in the index definition and , if one
 *                   expression does exist, it is checked to
 *                   see if it meets the constraints of being
 *                   part of an index
 * return :
 * parser(in) : parser context
 * node (in) : node - PT_CREATE_INDEX
 */
static void
pt_check_function_index_expr (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *col, *rem = NULL;
  int fnc_cnt = 0;
  int i = 0;

  for (col = node->info.index.column_names, i = 0; col != NULL; col = col->next, i++)
    {
      if (col->info.sort_spec.expr->node_type != PT_NAME)
    {
      if (pt_is_function_index_expr (parser, col->info.sort_spec.expr, true))
        {
          (void) parser_walk_tree (parser, col->info.sort_spec.expr, pt_check_function_index_expr_pre, NULL, NULL,
                       NULL);
          if (pt_has_error (parser))
        {
          /* Stop. */
          return;
        }
          node->info.index.function_expr = parser_copy_tree (parser, col);
          node->info.index.func_pos = i;
          rem = col;
        }
      else
        {
          return;
        }
      fnc_cnt++;
    }
    }
  if (fnc_cnt > 1)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_FUNCTION_INDEX);
    }
  else if (fnc_cnt > 0 && rem)
    {
      PT_NODE *list, *arg, *n;
      node->info.index.column_names = pt_remove_from_list (parser, rem, node->info.index.column_names);
      list = pt_expr_to_sort_spec (parser, node->info.index.function_expr->info.sort_spec.expr);

      for (arg = list; arg != NULL; arg = arg->next)
    {
      for (n = node->info.index.column_names; n != NULL; n = n->next)
        {
          if (!pt_str_compare (arg->info.sort_spec.expr->info.name.original,
                   n->info.sort_spec.expr->info.name.original, CASE_INSENSITIVE))
        {
          break;
        }
        }
      if (n == NULL)
        {
          PT_NODE *new_node = parser_copy_tree (parser, arg);
          new_node->next = NULL;
          node->info.index.column_names = parser_append_node (new_node, node->info.index.column_names);
          node->info.index.func_no_args++;
        }
    }
    }
}

/*
 * pt_check_function_index_expr_pre () - Helper function to check function
 *                   index semantic correctness.
 *
 * return         : Current node.
 * parser (in)        : Parser context.
 * node (in)          : Current node.
 * arg (in/out)       : Not used.
 * continue_walk (in/out) : Walker argument to know when to stop advancing.
 */
static PT_NODE *
pt_check_function_index_expr_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  assert (continue_walk != NULL);

  if (*continue_walk == PT_STOP_WALK || node == NULL)
    {
      return node;
    }
  if (node->node_type == PT_NAME && PT_IS_LTZ_TYPE (node->type_enum))
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NO_LTZ_IN_FUNCTION_FILTER_INDEX);
      *continue_walk = PT_STOP_WALK;
    }
  return node;
}

/*
 * pt_check_filter_index_expr () - verify if an expression tree is allowed
 *                 to be used in the filter expression of an
 *                 index
 * return : true if expression tree is valid, false otherwise
 * parser (in)  : parser context
 * atts (in): an attribute definition list
 * node (in) : root node of expression tree
 * db_obj (in) : class mop
 */
static void
pt_check_filter_index_expr (PARSER_CONTEXT * parser, PT_NODE * atts, PT_NODE * node, MOP db_obj)
{
  PT_FILTER_INDEX_INFO info;
  int atts_count = 0, i = 0;

  if (node == NULL)
    {
      /* null node; nothing to check */
      return;
    }

  info.atts = atts;
  while (atts)
    {
      atts_count++;
      atts = atts->next;
    }

  info.atts_count = atts_count;
  info.is_null_atts = (bool *) malloc (atts_count * sizeof (bool));
  if (info.is_null_atts == NULL)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      return;
    }
  for (i = 0; i < atts_count; i++)
    {
      info.is_null_atts[i] = false;
    }
  info.is_valid_expr = true;
  info.is_constant_expression = true;
  info.has_keys_in_expression = false;
  info.depth = 0;
  info.has_not = false;

  (void) parser_walk_tree (parser, node, pt_check_filter_index_expr_pre, &info, pt_check_filter_index_expr_post, &info);

  /* at least one key must be contained in filter expression (without IS_NULL operator, or variations like "not a is
   * not null") or at least one key must be not null constrained */
  if (info.is_valid_expr == true && info.is_constant_expression == false && info.has_keys_in_expression == false)
    {
      PT_NODE *attr = NULL, *p_nam = NULL;
      SM_CLASS *smclass = NULL;
      SM_ATTRIBUTE *smatt = NULL;
      int i;
      bool has_not_null_constraint;

      /* search for null constraints */
      i = 0;
      has_not_null_constraint = false;
      for (attr = info.atts; attr != NULL && has_not_null_constraint == false; attr = attr->next, i++)
    {
      if (attr->node_type == PT_SORT_SPEC)
        {
          p_nam = attr->info.sort_spec.expr;
        }
      else if (attr->node_type == PT_ATTR_DEF)
        {
          p_nam = attr->info.attr_def.attr_name;
        }

      if (p_nam == NULL || p_nam->node_type != PT_NAME)
        {
          continue;     /* give up */
        }

      if (smclass == NULL)
        {
          assert (db_obj != NULL);
          if (au_fetch_class (db_obj, &smclass, AU_FETCH_READ, AU_SELECT) != NO_ERROR)
        {
          info.is_valid_expr = false;
          break;
        }
        }

      for (smatt = smclass->attributes; smatt != NULL; smatt = (SM_ATTRIBUTE *) smatt->header.next)
        {
          if (SM_COMPARE_NAMES (smatt->header.name, p_nam->info.name.original) == 0)
        {
          if (db_attribute_is_non_null (smatt))
            {
              has_not_null_constraint = true;
            }
          break;
        }
        }
    }

      if (has_not_null_constraint == false)
    {
      info.is_valid_expr = false;
    }
    }
  if (info.is_valid_expr == false)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_FILTER_INDEX,
          pt_short_print (parser, node));
    }

  if (info.is_null_atts)
    {
      free_and_init (info.is_null_atts);
    }
}

/*
 * pt_check_filter_index_expr_post ()
 *
 * return : current node
 * parser (in)  : parser context
 * node (in)    : node
 * arg (in/out) : (is_valid_expr, expression depth)
 * continue_walk (in) : continue walk
 */
static PT_NODE *
pt_check_filter_index_expr_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_FILTER_INDEX_INFO *info = (PT_FILTER_INDEX_INFO *) arg;
  assert (info != NULL);

  switch (node->node_type)
    {
    case PT_EXPR:
      info->depth--;
      switch (node->info.expr.op)
    {
    case PT_NOT:
      info->has_not = !(info->has_not);
      break;

    default:
      break;
    }
      break;

    case PT_FUNCTION:
      info->depth--;
      break;

    default:
      break;
    }

  return node;
}

/*
 * pt_check_filter_index_expr () - verify if a node is allowed to be used
 *                 in the filter expression of an index
 * return : current node
 * parser (in)  : parser context
 * node (in)    : node
 * arg (in/out) : PT_FILTER_INDEX_INFO *
 * continue_walk (in) : continue walk
 */
static PT_NODE *
pt_check_filter_index_expr_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_FILTER_INDEX_INFO *info = (PT_FILTER_INDEX_INFO *) arg;
  assert (info != NULL);

  if (node == NULL || info->is_valid_expr == false)
    {
      return node;
    }
  *continue_walk = PT_CONTINUE_WALK;

  switch (node->node_type)
    {
    case PT_EXPR:
      info->depth++;
      /* only allow expressions that have a deterministic result */
      switch (node->info.expr.op)
    {
    case PT_AND:
    case PT_BETWEEN:
    case PT_NOT_BETWEEN:
    case PT_LIKE:
    case PT_NOT_LIKE:
    case PT_IS_IN:
    case PT_IS_NOT_IN:
    case PT_IS:
    case PT_IS_NOT:
    case PT_EQ_SOME:
    case PT_NE_SOME:
    case PT_GE_SOME:
    case PT_GT_SOME:
    case PT_LT_SOME:
    case PT_LE_SOME:
    case PT_EQ_ALL:
    case PT_NE_ALL:
    case PT_GE_ALL:
    case PT_GT_ALL:
    case PT_LT_ALL:
    case PT_LE_ALL:
    case PT_EQ:
    case PT_NE:
    case PT_GE:
    case PT_GT:
    case PT_LT:
    case PT_LE:
    case PT_NULLSAFE_EQ:
    case PT_PLUS:
    case PT_MINUS:
    case PT_TIMES:
    case PT_DIVIDE:
    case PT_UNARY_MINUS:
    case PT_BIT_NOT:
    case PT_BIT_XOR:
    case PT_BIT_AND:
    case PT_BIT_OR:
    case PT_BIT_COUNT:
    case PT_BITSHIFT_LEFT:
    case PT_BITSHIFT_RIGHT:
    case PT_DIV:
    case PT_MOD:
    case PT_XOR:
    case PT_BETWEEN_AND:
    case PT_BETWEEN_GE_LE:
    case PT_BETWEEN_GE_LT:
    case PT_BETWEEN_GT_LE:
    case PT_BETWEEN_GT_LT:
    case PT_BETWEEN_EQ_NA:
    case PT_BETWEEN_INF_LE:
    case PT_BETWEEN_INF_LT:
    case PT_BETWEEN_GE_INF:
    case PT_BETWEEN_GT_INF:
    case PT_RANGE:
    case PT_MODULUS:
    case PT_POSITION:
    case PT_SUBSTRING:
    case PT_OCTET_LENGTH:
    case PT_BIT_LENGTH:
    case PT_SUBSTRING_INDEX:
    case PT_SPACE:
    case PT_CHAR_LENGTH:
    case PT_LOWER:
    case PT_UPPER:
    case PT_TRIM:
    case PT_LTRIM:
    case PT_RTRIM:
    case PT_LPAD:
    case PT_RPAD:
    case PT_REPLACE:
    case PT_TRANSLATE:
    case PT_REPEAT:
    case PT_ADD_MONTHS:
    case PT_LAST_DAY:
    case PT_MONTHS_BETWEEN:
    case PT_TO_CHAR:
    case PT_TO_DATE:
    case PT_TO_NUMBER:
    case PT_TO_TIME:
    case PT_TO_TIMESTAMP:
    case PT_TO_DATETIME:
    case PT_EXTRACT:
    case PT_LIKE_ESCAPE:
    case PT_CAST:
    case PT_FLOOR:
    case PT_CEIL:
    case PT_SIGN:
    case PT_POWER:
    case PT_ROUND:
    case PT_ABS:
    case PT_TRUNC:
    case PT_CHR:
    case PT_INSTR:
    case PT_LEAST:
    case PT_GREATEST:
    case PT_STRCAT:
    case PT_DECODE:
    case PT_LOG:
    case PT_EXP:
    case PT_SQRT:
    case PT_CONCAT:
    case PT_CONCAT_WS:
    case PT_FIELD:
    case PT_LEFT:
    case PT_RIGHT:
    case PT_LOCATE:
    case PT_MID:
    case PT_STRCMP:
    case PT_REVERSE:
    case PT_ACOS:
    case PT_ASIN:
    case PT_ATAN:
    case PT_ATAN2:
    case PT_COS:
    case PT_SIN:
    case PT_COT:
    case PT_TAN:
    case PT_DEGREES:
    case PT_RADIANS:
    case PT_PI:
    case PT_FORMAT:
    case PT_LN:
    case PT_LOG2:
    case PT_LOG10:
    case PT_TIME_FORMAT:
    case PT_TIMESTAMP:
    case PT_FROM_UNIXTIME:
    case PT_ADDDATE:
    case PT_DATE_ADD:
    case PT_SUBDATE:
    case PT_DATE_SUB:
    case PT_DATE_FORMAT:
    case PT_DATEF:
    case PT_TIMEF:
    case PT_YEARF:
    case PT_MONTHF:
    case PT_DAYF:
    case PT_HOURF:
    case PT_MINUTEF:
    case PT_SECONDF:
    case PT_DAYOFMONTH:
    case PT_WEEKDAY:
    case PT_DAYOFWEEK:
    case PT_DAYOFYEAR:
    case PT_QUARTERF:
    case PT_TODAYS:
    case PT_FROMDAYS:
    case PT_TIMETOSEC:
    case PT_SECTOTIME:
    case PT_MAKEDATE:
    case PT_MAKETIME:
    case PT_WEEKF:
    case PT_USER:
    case PT_DATEDIFF:
    case PT_TIMEDIFF:
    case PT_STR_TO_DATE:
    case PT_DEFAULTF:
    case PT_LIKE_LOWER_BOUND:
    case PT_LIKE_UPPER_BOUND:
    case PT_BLOB_LENGTH:
    case PT_BLOB_TO_BIT:
    case PT_CLOB_LENGTH:
    case PT_CLOB_TO_CHAR:
    case PT_RLIKE:
    case PT_RLIKE_BINARY:
    case PT_NOT_RLIKE:
    case PT_NOT_RLIKE_BINARY:
    case PT_HEX:
    case PT_ADDTIME:
    case PT_ASCII:
    case PT_CONV:
    case PT_BIN:
    case PT_FINDINSET:
    case PT_INET_ATON:
    case PT_INET_NTOA:
    case PT_TZ_OFFSET:
    case PT_NEW_TIME:
    case PT_FROM_TZ:
    case PT_TO_DATETIME_TZ:
    case PT_TO_TIMESTAMP_TZ:
    case PT_CONV_TZ:
      /* valid expression, nothing to do */
      break;
    case PT_NOT:
      info->has_not = !(info->has_not);
      break;
    case PT_IS_NULL:
    case PT_IS_NOT_NULL:
      {
        PT_NODE *attr = NULL, *p_nam = NULL;
        PT_NODE *arg1 = NULL;
        int i = 0, j = 0;

        if (node->info.expr.op == PT_IS_NULL)
          {
        if (info->has_not == true)
          {
            /* not expression is null case */
            break;
          }
          }
        else if (info->has_not == false)
          {
        /* expression is not null case */
        break;
          }

        arg1 = node->info.expr.arg1;
        if (arg1 == NULL || info->atts == NULL)
          {
        break;
          }

        if (arg1->node_type != PT_NAME)
          {
        /* do not allow expression is null do not allow not expression is not null */
        info->is_valid_expr = false;

        break;
          }

        /* arg1 is PT_NAME node */
        if (arg1->info.name.original == NULL)
          {
        break;
          }

        i = 0;
        for (attr = info->atts; attr != NULL; attr = attr->next, i++)
          {
        if (attr->node_type == PT_SORT_SPEC)
          {
            p_nam = attr->info.sort_spec.expr;
          }
        else if (attr->node_type == PT_ATTR_DEF)
          {
            p_nam = attr->info.attr_def.attr_name;
          }

        if (p_nam == NULL || p_nam->node_type != PT_NAME)
          {
            continue;   /* give up */
          }

        if (!pt_str_compare (p_nam->info.name.original, arg1->info.name.original, CASE_INSENSITIVE))
          {
            info->is_null_atts[i] = true;
            for (j = 0; j < info->atts_count; j++)
              {
            if (info->is_null_atts[j] == false)
              {
                break;
              }
              }
            if (j == info->atts_count)
              {
            info->is_valid_expr = false;
              }

            break;
          }
          }
      }
      break;

    case PT_FUNCTION_HOLDER:
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC,
              MSGCAT_SEMANTIC_FUNCTION_CANNOT_BE_USED_FOR_FILTER_INDEX,
              fcode_get_lowercase_name (node->info.expr.arg1->info.function.function_type));
      info->is_valid_expr = false;
      break;

    default:
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC,
              MSGCAT_SEMANTIC_FUNCTION_CANNOT_BE_USED_FOR_FILTER_INDEX, pt_show_binopcode (node->info.expr.op));
      info->is_valid_expr = false;
      break;
    }
      break;

    case PT_FUNCTION:
      info->depth++;
      /* do not allow aggregates and analytic functions */
      switch (node->info.function.function_type)
    {
    case F_SET:
    case F_MULTISET:
    case F_SEQUENCE:
      /* the functions above are used in the argument IN (values list) expression */
    case F_ELT:
    case F_INSERT_SUBSTRING:
    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:
      /* valid expression, nothing to do */
      break;
    default:
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC,
              MSGCAT_SEMANTIC_FUNCTION_CANNOT_BE_USED_FOR_FILTER_INDEX,
              fcode_get_lowercase_name (node->info.function.function_type));
      info->is_valid_expr = false;
      break;
    }
      break;

    case PT_NAME:
      /* only allow attribute names */
      info->is_constant_expression = false;
      if (node->info.name.meta_class != PT_META_ATTR && node->info.name.meta_class != PT_NORMAL)
    {
      /* valid expression, nothing to do */
      info->is_valid_expr = false;
    }
      else if (PT_IS_LTZ_TYPE (node->type_enum))
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NO_LTZ_IN_FUNCTION_FILTER_INDEX);
      info->is_valid_expr = false;
    }
      else if (info->has_keys_in_expression == false)
    {
      PT_NODE *attr = NULL, *p_nam = NULL;
      int i;
      i = 0;
      for (attr = info->atts; attr != NULL; attr = attr->next, i++)
        {
          if (attr->node_type == PT_SORT_SPEC)
        {
          p_nam = attr->info.sort_spec.expr;
        }
          else if (attr->node_type == PT_ATTR_DEF)
        {
          p_nam = attr->info.attr_def.attr_name;
        }

          if (p_nam == NULL || p_nam->node_type != PT_NAME)
        {
          continue; /* give up */
        }

          if ((pt_str_compare (p_nam->info.name.original, node->info.name.original, CASE_INSENSITIVE) == 0)
          && (info->is_null_atts[i] == 0))
        {
          info->has_keys_in_expression = true;
          break;
        }
        }
    }
      break;

    case PT_VALUE:
      if (info->depth == 0)
    {
      if (node->info.value.db_value_is_initialized && DB_VALUE_TYPE (&node->info.value.db_value) == DB_TYPE_INTEGER)
        {
          if (db_get_int (&node->info.value.db_value) == 0)
        {
          info->is_valid_expr = false;
        }
        }
    }
      break;
    case PT_DATA_TYPE:
      /* valid expression, nothing to do */
      break;

    default:
      info->is_valid_expr = false;
      break;
    }

  if (info->is_valid_expr == false)
    {
      *continue_walk = PT_STOP_WALK;
    }

  return node;
}

/*
 * pt_get_assignments () - get assignment list for INSERT/UPDATE/MERGE
 *             statements
 * return   : assignment list or NULL
 * node (in): statement node
 */
static PT_NODE *
pt_get_assignments (PT_NODE * node, bool * dblinked)
{
  PT_NODE *spec, *assignment;

  *dblinked = false;

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

  switch (node->node_type)
    {
    case PT_UPDATE:
      spec = node->info.update.spec;
      assignment = node->info.update.assignment;
      break;
    case PT_MERGE:
      spec = node->info.merge.into;
      assignment = node->info.merge.update.assignment;
      break;
    case PT_INSERT:
      spec = node->info.insert.spec;
      assignment = node->info.insert.odku_assignments;
      break;
    default:
      return NULL;
    }

  if (spec && spec->info.spec.remote_server_name)
    {
      *dblinked = true;
    }

  return assignment;
}

/*
 * pt_check_odku_assignments () - check ON DUPLICATE KEY assignments of an
 *                INSERT statement
 * return : node
 * parser (in) : parser context
 * insert (in) : insert statement
 *
 * Note: this function performs the following validations:
 *    - there are no double assignments
 *    - assignments are only performed to columns belonging to the insert spec
 *    - only instance attributes are updated
 */
PT_NODE *
pt_check_odku_assignments (PARSER_CONTEXT * parser, PT_NODE * insert)
{
  PT_NODE *assignment, *spec, *lhs;
  if (insert == NULL || insert->node_type != PT_INSERT)
    {
      return insert;
    }

  if (insert->info.insert.odku_assignments == NULL)
    {
      return insert;
    }

  pt_no_double_updates (parser, insert);
  if (pt_has_error (parser))
    {
      return NULL;
    }
  pt_check_assignments (parser, insert);
  if (pt_has_error (parser))
    {
      return NULL;
    }

  spec = insert->info.insert.spec;
  for (assignment = insert->info.insert.odku_assignments; assignment != NULL; assignment = assignment->next)
    {
      if (assignment->node_type != PT_EXPR || assignment->info.expr.op != PT_ASSIGN)
    {
      assert (false);
      PT_INTERNAL_ERROR (parser, "semantic");
      return NULL;
    }
      lhs = assignment->info.expr.arg1;
      if (lhs == NULL || lhs->node_type != PT_NAME)
    {
      assert (false);
      PT_INTERNAL_ERROR (parser, "semantic");
      return NULL;
    }
      if (lhs->info.name.spec_id != spec->info.spec.id)
    {
      PT_ERRORm (parser, lhs, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ILLEGAL_LHS);
      return NULL;
    }
    }
  return insert;
}

/*
 * pt_check_vacuum () - Check VACUUM statement.
 *
 * return      : Void.
 * parser (in) : Parser context.
 * node (in)   : VACUUM parse tree node.
 */
static void
pt_check_vacuum (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_FLAT_SPEC_INFO info;

  assert (parser != NULL);
  if (!PT_IS_VACUUM_NODE (node))
    {
      /* Not the scope of this function */
      return;
    }

  info.spec_parent = NULL;
  info.for_update = false;
  /* Replace each Entity Spec with an Equivalent flat list */
  parser_walk_tree (parser, node, pt_flat_spec_pre, &info, pt_continue_walk, NULL);
}

/*
 * pt_get_select_list_coll_compat () - scans a UNION parse tree and retains
 *                     for each column with collation the node
 *                     (and its compatibility info) having the
 *                     least (collation) coercible level
 *
 *   return:  compatibility status
 *   parser(in): the parser context
 *   query(in): query node
 *   cinfo(in/out): compatibility info array structure
 *   num_cinfo(in): number of elements in cinfo
 */
static PT_UNION_COMPATIBLE
pt_get_select_list_coll_compat (PARSER_CONTEXT * parser, PT_NODE * query, SEMAN_COMPATIBLE_INFO * cinfo, int num_cinfo)
{
  PT_NODE *attrs, *att;
  int i;
  PT_UNION_COMPATIBLE status = PT_UNION_COMP, status2;

  assert (query != NULL);

  switch (query->node_type)
    {
    case PT_SELECT:

      attrs = pt_get_select_list (parser, query);

      for (att = attrs, i = 0; i < num_cinfo && att != NULL; ++i, att = att->next)
    {
      SEMAN_COMPATIBLE_INFO cinfo_att;

      if (!PT_HAS_COLLATION (att->type_enum))
        {
          continue;
        }

      pt_get_compatible_info_from_node (att, &cinfo_att);

      if (cinfo[i].type_enum == PT_TYPE_NONE)
        {
          /* first query, init this column */
          memcpy (&(cinfo[i]), &cinfo_att, sizeof (cinfo_att));
        }
      else if (cinfo_att.coll_infer.coerc_level < cinfo[i].coll_infer.coerc_level)
        {
          assert (PT_HAS_COLLATION (cinfo[i].type_enum));

          memcpy (&(cinfo[i].coll_infer), &(cinfo_att.coll_infer), sizeof (cinfo_att.coll_infer));
          cinfo[i].ref_att = att;
          status = PT_UNION_INCOMP;
        }
      else if (cinfo_att.coll_infer.coll_id != cinfo[i].coll_infer.coll_id)
        {
          status = PT_UNION_INCOMP;
        }
    }
      break;
    case PT_UNION:
    case PT_INTERSECTION:
    case PT_DIFFERENCE:
      status = pt_get_select_list_coll_compat (parser, query->info.query.q.union_.arg1, cinfo, num_cinfo);

      if (status != PT_UNION_COMP && status != PT_UNION_INCOMP)
    {
      break;
    }

      status2 = pt_get_select_list_coll_compat (parser, query->info.query.q.union_.arg2, cinfo, num_cinfo);
      if (status2 != PT_UNION_COMP)
    {
      status = status2;
    }
      break;
    default:
      break;
    }

  return status;
}

/*
 * pt_apply_union_select_list_collation () - scans a UNION parse tree and
 *      sets for each node with collation the collation corresponding
 *      of the column in 'cinfo' array
 *
 *   return:  union compatibility status
 *   parser(in): the parser context
 *   query(in): query node
 *   cinfo(in): compatibility info array structure
 *   num_cinfo(in): number of elements in cinfo
 */
static PT_UNION_COMPATIBLE
pt_apply_union_select_list_collation (PARSER_CONTEXT * parser, PT_NODE * query, SEMAN_COMPATIBLE_INFO * cinfo,
                      int num_cinfo)
{
  PT_NODE *attrs, *att, *prev_att, *next_att, *new_att;
  int i;
  PT_UNION_COMPATIBLE status = PT_UNION_COMP, status2;

  assert (query != NULL);

  switch (query->node_type)
    {
    case PT_SELECT:

      attrs = pt_get_select_list (parser, query);

      prev_att = NULL;
      next_att = NULL;

      for (att = attrs, i = 0; i < num_cinfo && att != NULL; ++i, prev_att = att, att = next_att)
    {
      SEMAN_COMPATIBLE_INFO cinfo_att;

      next_att = att->next;

      if (!PT_HAS_COLLATION (att->type_enum))
        {
          continue;
        }

      assert (PT_HAS_COLLATION (cinfo[i].type_enum));

      pt_get_compatible_info_from_node (att, &cinfo_att);

      if (cinfo_att.coll_infer.coll_id != cinfo[i].coll_infer.coll_id)
        {
          bool new_cast_added = false;

          if (pt_common_collation (&cinfo_att.coll_infer, &(cinfo[i].coll_infer), NULL, 2, false,
                       &cinfo_att.coll_infer.coll_id, &cinfo_att.coll_infer.codeset) != 0)
        {
          PT_ERRORmf2 (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UNION_INCOMPATIBLE,
                   pt_short_print (parser, att), pt_short_print (parser, cinfo[i].ref_att));
          return PT_UNION_INCOMP_CANNOT_FIX;
        }

          new_att = pt_make_cast_with_compatible_info (parser, att, next_att, &cinfo_att, &new_cast_added);
          if (new_att == NULL)
        {
          PT_ERRORmf2 (parser, att, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UNION_INCOMPATIBLE,
                   pt_short_print (parser, att), pt_short_print (parser, cinfo[i].ref_att));
          return PT_UNION_INCOMP_CANNOT_FIX;
        }

          att = new_att;

          if (new_cast_added)
        {
          if (prev_att == NULL)
            {
              query->info.query.q.select.list = att;
              query->type_enum = att->type_enum;
              if (query->data_type)
            {
              parser_free_tree (parser, query->data_type);
            }

              query->data_type = parser_copy_tree_list (parser, att->data_type);
            }
          else
            {
              prev_att->next = att;
            }
        }

          status = PT_UNION_INCOMP;
        }
    }
      break;
    case PT_UNION:
    case PT_INTERSECTION:
    case PT_DIFFERENCE:
      status = pt_apply_union_select_list_collation (parser, query->info.query.q.union_.arg1, cinfo, num_cinfo);
      if (status != PT_UNION_COMP && status != PT_UNION_INCOMP)
    {
      return status;
    }

      status2 = pt_apply_union_select_list_collation (parser, query->info.query.q.union_.arg2, cinfo, num_cinfo);

      if (status2 != PT_UNION_COMP && status2 != PT_UNION_INCOMP)
    {
      return status;
    }

      if (status2 != PT_UNION_COMP)
    {
      status = status2;
    }

      if (status == PT_UNION_INCOMP)
    {
      if (query->data_type != NULL)
        {
          parser_free_tree (parser, query->data_type);
        }

      query->data_type = parser_copy_tree (parser, query->info.query.q.union_.arg1->data_type);
    }
      break;
    default:
      break;
    }

  return status;
}

/*
 * pt_mark_union_leaf_nodes () - walking function for setting for each UNION
 *               DIFFERENCE/INTERSECTION query if it is a root
 *               of a leaf node.
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_mark_union_leaf_nodes (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  if (PT_IS_UNION (node) || PT_IS_INTERSECTION (node) || PT_IS_DIFFERENCE (node))
    {
      PT_NODE *arg;

      arg = node->info.query.q.union_.arg1;
      if (PT_IS_UNION (arg) || PT_IS_INTERSECTION (arg) || PT_IS_DIFFERENCE (arg))
    {
      arg->info.query.q.union_.is_leaf_node = 1;
    }

      arg = node->info.query.q.union_.arg2;
      if (PT_IS_UNION (arg) || PT_IS_INTERSECTION (arg) || PT_IS_DIFFERENCE (arg))
    {
      arg->info.query.q.union_.is_leaf_node = 1;
    }
    }

  return node;
}

/*
 * pt_check_where () -  do semantic checks on a node's where clause
 *   return:
 *   parser(in): the parser context
 *   node(in): the node to check
 */
static PT_NODE *
pt_check_where (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *function = NULL;

  /* check if exists aggregate/analytic functions in where clause */
  function = pt_find_aggregate_analytic_in_where (parser, node);
  if (function != NULL)
    {
      if (pt_is_aggregate_function (parser, function))
    {
      PT_ERRORm (parser, function, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_AGGREGATE);
    }
      else
    {
      PT_ERRORm (parser, function, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NESTED_ANALYTIC_FUNCTIONS);
    }
    }

  return node;
}

/*
 * pt_check_range_partition_strict_increasing () -
 *   return: NO_ERROR or error code
 *   parser(in): the parser context
 *   stmt(in): the top statement
 *   part(in): current partition
 *   part_next(in): the next partition
 *   column_dt(in): data type
 *
 */
static int
pt_check_range_partition_strict_increasing (PARSER_CONTEXT * parser, PT_NODE * stmt, PT_NODE * part,
                        PT_NODE * part_next, PT_NODE * column_dt)
{
  int error = NO_ERROR;
  PT_NODE *pt_val1 = NULL, *pt_val2 = NULL;
  DB_VALUE *db_val1 = NULL, *db_val2 = NULL;
  DB_VALUE null_val;
  DB_VALUE_COMPARE_RESULT compare_result;

  assert (parser != NULL && stmt != NULL && part != NULL && part->node_type == PT_PARTS);
  assert (part_next == NULL || part_next->node_type == PT_PARTS);
  assert (column_dt == NULL || column_dt->node_type == PT_DATA_TYPE);

  /* make sure value1 is the same type described by column_dt */
  pt_val1 = part->info.parts.values;
  assert (pt_val1 == NULL || column_dt == NULL
      || (pt_val1->type_enum == column_dt->type_enum
          && (!PT_IS_STRING_TYPE (pt_val1->type_enum)
          || (pt_val1->data_type == NULL
              || (pt_val1->data_type->info.data_type.collation_id ==
              column_dt->info.data_type.collation_id)))));

  db_make_null (&null_val);

  /* next value */
  if (part_next != NULL)
    {
      /* the first value */
      db_val1 = pt_value_to_db (parser, pt_val1);
      if (db_val1 == NULL)
    {
      if (pt_has_error (parser))
        {
          error = MSGCAT_SEMANTIC_PARTITION_RANGE_INVALID;

          PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, error, part->info.parts.name->info.name.original);

          goto end;
        }

      db_val1 = &null_val;
    }

      pt_val2 = part_next->info.parts.values;

      /* if column_dt is not null, do the cast if needed */
      if (pt_val2 != NULL && column_dt != NULL)
    {
      error = pt_coerce_partition_value_with_data_type (parser, pt_val2, column_dt);

      if (error != NO_ERROR)
        {
          error = MSGCAT_SEMANTIC_CONSTANT_TYPE_MISMATCH;

          PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, error,
              part_next->info.parts.name->info.name.original);

          goto end;
        }
    }

      db_val2 = pt_value_to_db (parser, pt_val2);
      if (db_val2 == NULL)
    {
      if (pt_has_error (parser))
        {
          error = MSGCAT_SEMANTIC_PARTITION_RANGE_INVALID;

          PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, error,
              part_next->info.parts.name->info.name.original);

          goto end;
        }

      db_val2 = &null_val;
    }

      /* check the strict increasing */
      if (DB_IS_NULL (db_val1))
    {
      /* this is an error */
      error = MSGCAT_SEMANTIC_PARTITION_RANGE_ERROR;

      PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, error, part);

      goto end;
    }
      else if (!DB_IS_NULL (db_val2))
    {
      compare_result = (DB_VALUE_COMPARE_RESULT) db_value_compare (db_val1, db_val2);
      if (compare_result != DB_LT)
        {
          /* this is an error */
          error = MSGCAT_SEMANTIC_PARTITION_RANGE_ERROR;

          PT_ERRORmf (parser, stmt, MSGCAT_SET_PARSER_SEMANTIC, error, part_next);

          goto end;
        }
    }
    }

end:

  return error;
}

/*
 * pt_coerce_partition_value_with_data_type () - do the cast
 *   return: NO_ERROR or error code
 *   parser(in):
 *   value(in):
 *   data_type(in):
 *
 */
static int
pt_coerce_partition_value_with_data_type (PARSER_CONTEXT * parser, PT_NODE * value, PT_NODE * data_type)
{
  int error = NO_ERROR;

  assert (value != NULL && data_type != NULL && data_type->node_type == PT_DATA_TYPE);

  if (value->type_enum != data_type->type_enum
      || (PT_IS_STRING_TYPE (value->type_enum) && value->data_type != NULL
      && value->data_type->info.data_type.collation_id != data_type->info.data_type.collation_id))
    {
      error = pt_coerce_value (parser, value, value, data_type->type_enum, data_type);
    }

  return error;
}

/*
 * pt_try_remove_order_by - verify and remove order_by clause after it has been decided it is unnecessary
 *  return: void
 *  parser(in): Parser context
 *  query(in/out): Processed query
 */
void
pt_try_remove_order_by (PARSER_CONTEXT * parser, PT_NODE * query)
{
  PT_NODE *col, *next;

  assert (PT_IS_QUERY_NODE_TYPE (query->node_type));

  if (query->info.query.order_by == NULL || query->info.query.orderby_for != NULL || query->info.query.limit != NULL)
    {
      /* order_by can not be removed when query has orderby_for or limit */
      return;
    }

  /* if select list has orderby_num(), can not remove ORDER BY clause for example:
   * (i, j) = (select i, orderby_num() from t order by i)
   */
  for (col = pt_get_select_list (parser, query); col; col = col->next)
    {
      if (col->node_type == PT_EXPR && col->info.expr.op == PT_ORDERBY_NUM)
    {
      break;        /* can not remove ORDER BY clause */
    }
    }

  if (!col)
    {
      /* no column is ORDERBY_NUM, order_by can be removed */
      parser_free_tree (parser, query->info.query.order_by);
      query->info.query.order_by = NULL;
      query->info.query.flag.order_siblings = 0;

      for (col = pt_get_select_list (parser, query); col && col->next; col = next)
    {
      next = col->next;
      if (next->flag.is_hidden_column)
        {
          parser_free_tree (parser, next);
          col->next = NULL;
          break;
        }
    }
    }
}


/*
 * pt_check_union_is_foldable - decide if union can be folded
 *
 *  return : union foldability
 *  parser(in) : Parser context.
 *  union_node(in) : Union node.
 */
STATEMENT_SET_FOLD
pt_check_union_is_foldable (PARSER_CONTEXT * parser, PT_NODE * union_node)
{
  PT_NODE *arg1, *arg2;
  bool arg1_is_null, arg2_is_null;
  STATEMENT_SET_FOLD fold_as;

  assert (union_node->node_type == PT_UNION || union_node->node_type == PT_INTERSECTION
      || union_node->node_type == PT_DIFFERENCE || union_node->node_type == PT_CTE);

  if (union_node->node_type == PT_CTE)
    {
      /* A CTE is a union between the non_recursive and recursive parts */
      return STATEMENT_SET_FOLD_NOTHING;
    }

  arg1 = union_node->info.query.q.union_.arg1;
  arg2 = union_node->info.query.q.union_.arg2;

  arg1_is_null = pt_false_where (parser, arg1);
  arg2_is_null = pt_false_where (parser, arg2);

  if (!arg1_is_null && !arg2_is_null)
    {
      /* nothing to fold at this moment. */
      fold_as = STATEMENT_SET_FOLD_NOTHING;
    }
  else if (arg1_is_null && arg2_is_null)
    {
      /* fold the entire node as null */
      fold_as = STATEMENT_SET_FOLD_AS_NULL;
    }
  else if (arg1_is_null && !arg2_is_null)
    {
      if (union_node->node_type == PT_UNION)
    {
      fold_as = STATEMENT_SET_FOLD_AS_ARG2;
    }
      else
    {
      fold_as = STATEMENT_SET_FOLD_AS_NULL;
    }
    }
  else
    {
      assert (!arg1_is_null && arg2_is_null);

      if (union_node->node_type == PT_UNION || union_node->node_type == PT_DIFFERENCE)
    {
      fold_as = STATEMENT_SET_FOLD_AS_ARG1;
    }
      else
    {
      assert (union_node->node_type == PT_INTERSECTION);
      fold_as = STATEMENT_SET_FOLD_AS_NULL;
    }
    }

  /* check if folding would require merging WITH clauses; in this case we give up folding */
  if (fold_as == STATEMENT_SET_FOLD_AS_ARG1 || fold_as == STATEMENT_SET_FOLD_AS_ARG2)
    {
      PT_NODE *active = (fold_as == STATEMENT_SET_FOLD_AS_ARG1 ? arg1 : arg2);

      if (PT_IS_QUERY_NODE_TYPE (active->node_type) && active->info.query.with != NULL
      && union_node->info.query.with != NULL)
    {
      fold_as = STATEMENT_SET_FOLD_NOTHING;
    }
    }

  return fold_as;
}

/*
 * pt_fold_union - fold union node following indicated method
 *
 *  return : union node folded
 *  parser (in) : Parser context.
 *  union_node (in) : Union node.
 *  fold_as (in) : indicated folding method
 */
PT_NODE *
pt_fold_union (PARSER_CONTEXT * parser, PT_NODE * union_node, STATEMENT_SET_FOLD fold_as)
{
  PT_NODE *new_node, *next;
  int line, column;
  const char *alias_print;

  assert (union_node->node_type == PT_UNION || union_node->node_type == PT_INTERSECTION
      || union_node->node_type == PT_DIFFERENCE);

  line = union_node->line_number;
  column = union_node->column_number;
  alias_print = union_node->alias_print;
  next = union_node->next;
  union_node->next = NULL;

  if (fold_as == STATEMENT_SET_FOLD_AS_NULL)
    {
      /* fold the statement set as null, we don't need to fold orderby clause because we return null. */
      parser_free_tree (parser, union_node);
      new_node = parser_new_node (parser, PT_VALUE);
      new_node->type_enum = PT_TYPE_NULL;
    }
  else if (fold_as == STATEMENT_SET_FOLD_AS_ARG1 || fold_as == STATEMENT_SET_FOLD_AS_ARG2)
    {
      PT_NODE *active;
      PT_NODE *union_orderby, *union_orderby_for, *union_limit, *union_with_clause;
      unsigned int union_rewrite_limit;

      if (fold_as == STATEMENT_SET_FOLD_AS_ARG1)
    {
      pt_move_node (active, union_node->info.query.q.union_.arg1);
    }
      else
    {
      pt_move_node (active, union_node->info.query.q.union_.arg2);
    }

      /* to save union's orderby or limit clause to arg1 or arg2 */
      pt_move_node (union_orderby, union_node->info.query.order_by);
      pt_move_node (union_orderby_for, union_node->info.query.orderby_for);
      pt_move_node (union_limit, union_node->info.query.limit);
      union_rewrite_limit = union_node->info.query.flag.rewrite_limit;
      pt_move_node (union_with_clause, union_node->info.query.with);

      /* When active node has a limit or orderby_for clause and union node has a limit or ORDERBY clause, need a
       * derived table to keep both conflicting clauses. When a subquery has orderby clause without
       * limit/orderby_for, it may be ignored for meaningless cases. */
      if ((active->info.query.limit || active->info.query.orderby_for) && (union_limit || union_orderby))
    {
      PT_NODE *derived;

      derived = mq_rewrite_query_as_derived (parser, active);
      if (derived == NULL)
        {
          assert (pt_has_error (parser));
          return NULL;
        }

      new_node = derived;
    }
      else
    {
      /* just use active node */
      new_node = active;
    }

      /* free union node */
      parser_free_tree (parser, union_node);

      /* to fold the query with remaining parts */
      if (union_orderby != NULL)
    {
      new_node->info.query.order_by = union_orderby;
      new_node->info.query.orderby_for = union_orderby_for;
    }
      if (union_limit != NULL)
    {
      new_node->info.query.limit = union_limit;
      new_node->info.query.flag.rewrite_limit = union_rewrite_limit;
    }
      if (union_with_clause)
    {
      assert (new_node->info.query.with == NULL);
      new_node->info.query.with = union_with_clause;
    }

      /* check the union's orderby or limit clause if present */
      pt_check_order_by (parser, new_node);
    }
  else
    {
      assert (fold_as == STATEMENT_SET_FOLD_NOTHING);

      /* do nothing */
      new_node = union_node;
    }

  new_node->line_number = line;
  new_node->column_number = column;
  new_node->alias_print = alias_print;
  new_node->next = next;

  return new_node;
}