Skip to content

File name_resolution.c

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

/*
 * name_resolution.c - resolving related functions
 */

#ident "$Id$"

#include "config.h"

#include <assert.h>
#include <unordered_map>

#include "authenticate.h"
#include "porting.h"
#include "error_manager.h"
#include "parser.h"
#include "parser_message.h"
#include "semantic_check.h"
#include "dbtype.h"
#include "object_domain.h"
#include "object_primitive.h"
#include "memory_alloc.h"
#include "intl_support.h"
#include "memory_hash.h"
#include "system_parameter.h"
#include "object_print.h"
#include "jsp_cl.h"
#include "execute_schema.h"
#include "schema_manager.h"
#include "schema_system_catalog_constants.h"
#include "execute_statement.h"
#include "show_meta.h"
#include "network_interface_cl.h"
#include "locator_cl.h"
#include "db_json.hpp"

#include "dbtype.h"

#ifndef DBDEF_HEADER_
#define DBDEF_HEADER_
#endif

#include <cas_cci.h>

extern "C"
{
  extern int parser_function_code;
  extern size_t json_table_column_count;
}

#define PT_NAMES_HASH_SIZE                50

typedef struct extra_specs_frame PT_EXTRA_SPECS_FRAME;
struct extra_specs_frame
{
  struct extra_specs_frame *next;
  PT_NODE *extra_specs;
};

typedef struct scopes SCOPES;
struct scopes
{
  SCOPES *next;         /* next outermost scope */
  PT_NODE *specs;       /* list of PT_SPEC nodes */
  unsigned short correlation_level;
  /* how far up the stack was a name found? */
  short location;       /* for outer join */
};

typedef struct pt_bind_names_arg PT_BIND_NAMES_ARG;
struct pt_bind_names_arg
{
  SCOPES *scopes;
  PT_EXTRA_SPECS_FRAME *spec_frames;
  SEMANTIC_CHK_INFO *sc_info;
};

typedef struct natural_join_attr_info NATURAL_JOIN_ATTR_INFO;
struct natural_join_attr_info
{
  char *name;
  PT_TYPE_ENUM type_enum;
  PT_MISC_TYPE meta_class;
  NATURAL_JOIN_ATTR_INFO *next;
};

enum
{
  REQUIRE_ALL_MATCH = false,
  DISCARD_NO_MATCH = true
};

static const char *CPTR_PT_NAME_IN_GROUP_HAVING = "name_in_group_having";

typedef struct pt_bind_names_data_type PT_BIND_NAMES_DATA_TYPE;
struct pt_bind_names_data_type
{
  PT_TYPE_ENUM type_enum;
  PT_NODE *data_type;
};

static PT_NODE *pt_bind_parameter (PARSER_CONTEXT * parser, PT_NODE * parameter);
static PT_NODE *pt_bind_parameter_path (PARSER_CONTEXT * parser, PT_NODE * path);
static PT_NODE *pt_bind_name_or_path_in_scope (PARSER_CONTEXT * parser, PT_BIND_NAMES_ARG * bind_arg,
                           PT_NODE * in_node);
static void pt_bind_type_of_host_var (PARSER_CONTEXT * parser, PT_NODE * hv);
static void pt_bind_spec_attrs (PARSER_CONTEXT * parser, PT_NODE * spec);
static void pt_bind_scope (PARSER_CONTEXT * parser, PT_BIND_NAMES_ARG * bind_arg);
static FUNC_CODE pt_find_function_type (const char *name);
static PT_NODE *pt_mark_location (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_bind_names_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_check_Oracle_outerjoin (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_clear_Oracle_outerjoin_spec_id (PARSER_CONTEXT * parser, PT_NODE * node, void *arg,
                           int *continue_walk);
static PT_NODE *pt_bind_names (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_bind_value_to_hostvar_local (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static int pt_find_attr_in_class_list (PARSER_CONTEXT * parser, PT_NODE * flat, PT_NODE * attr);
static int pt_find_class_attribute (PARSER_CONTEXT * parser, PT_NODE * cls, PT_NODE * attr);
static int pt_find_name_in_spec (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * name);
static int pt_check_unique_exposed (PARSER_CONTEXT * parser, const PT_NODE * p);
static PT_NODE *pt_common_attribute (PARSER_CONTEXT * parser, PT_NODE * p, PT_NODE * q);
static PT_NODE *pt_get_all_attributes_and_types (PARSER_CONTEXT * parser, PT_NODE * cls, PT_NODE * from);
static PT_NODE *pt_get_all_json_table_attributes_and_types (PARSER_CONTEXT * parser, PT_NODE * json_table_node,
                                const char *json_table_alias);
static PT_NODE *pt_json_table_gather_attribs (PARSER_CONTEXT * parser, PT_NODE * json_table_node, void *args,
                          int *continue_walk);
static PT_NODE *pt_dblink_table_gather_attribs (PARSER_CONTEXT * parser, PT_NODE * dblink_column, void *args,
                        int *continue_walk);
static PT_NODE *pt_get_all_dblink_table_attributes_and_types (PARSER_CONTEXT * parser, PT_NODE * dblink_cols,
                                  const char *dblink_table_alias);
static PT_NODE *pt_get_all_showstmt_attributes_and_types (PARSER_CONTEXT * parser, PT_NODE * derived_table);
static void pt_get_attr_data_type (PARSER_CONTEXT * parser, DB_ATTRIBUTE * att, PT_NODE * attr);
static PT_NODE *pt_unwhacked_spec (PARSER_CONTEXT * parser, PT_NODE * scope, PT_NODE * spec);
static PT_NODE *pt_resolve_correlation (PARSER_CONTEXT * parser, PT_NODE * in_node, PT_NODE * scope,
                    PT_NODE * exposed_spec, int col_name, PT_NODE ** p_entity);
static PT_NODE *pt_resolve_partition_spec (PARSER_CONTEXT * parser, PT_NODE * partition_spec, PT_NODE * spec_parent,
                       bool fo_update);
static int pt_spec_in_domain (PT_NODE * cls, PT_NODE * lst);
static PT_NODE *pt_get_resolution (PARSER_CONTEXT * parser, PT_BIND_NAMES_ARG * bind_arg, PT_NODE * scope,
                   PT_NODE * in_node, PT_NODE ** p_entity, int col_name);
static PT_NODE *pt_expand_external_path (PARSER_CONTEXT * parser, PT_NODE * in_node, PT_NODE ** p_entity);
static PT_NODE *pt_is_correlation_name (PARSER_CONTEXT * parser, PT_NODE * scope, PT_NODE * nam);
static PT_NODE *pt_find_path_entity (PARSER_CONTEXT * parser, PT_NODE * scope, PT_NODE * match);
static PT_NODE *pt_is_on_list (PARSER_CONTEXT * parser, const PT_NODE * p, const PT_NODE * list);
static PT_NODE *pt_name_list_union (PARSER_CONTEXT * parser, PT_NODE * list, PT_NODE * additions);
static PT_NODE *pt_name_list_diff (PARSER_CONTEXT * parser, PT_NODE * list, PT_NODE * deletions);
static PT_NODE *pt_make_subclass_list (PARSER_CONTEXT * parser, DB_OBJECT * db, int line_num, int col_num, UINTPTR id,
                       PT_MISC_TYPE meta_class, MHT_TABLE * names_mht);
static PT_NODE *pt_make_flat_name_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * spec_parent,
                    bool for_update);
static int pt_must_have_exposed_name (PARSER_CONTEXT * parser, PT_NODE * p);
static PT_NODE *pt_object_to_data_type (PARSER_CONTEXT * parser, PT_NODE * class_list);
static int pt_resolve_hint_args (PARSER_CONTEXT * parser, PT_NODE ** arg_list, PT_NODE * spec_list,
                 bool discard_no_match);
static int pt_resolve_hint (PARSER_CONTEXT * parser, PT_NODE * node);
static PT_NODE *pt_copy_data_type_entity (PARSER_CONTEXT * parser, PT_NODE * data_type);
static PT_NODE *pt_insert_conjunct (PARSER_CONTEXT * parser, PT_NODE * path_dot, PT_NODE * prev_entity);
static PT_NODE *pt_lookup_entity (PARSER_CONTEXT * parser, PT_NODE * path_entities, PT_NODE * expr);

static bool pt_resolve_method_type (PARSER_CONTEXT * parser, PT_NODE * node);
static PT_NODE *pt_make_method_call (PARSER_CONTEXT * parser, PT_NODE * node, PT_BIND_NAMES_ARG * bind_arg);
static PT_NODE *pt_resolve_method (PARSER_CONTEXT * parser, PT_NODE * node, PT_BIND_NAMES_ARG * bind_arg);
static PT_NODE *pt_resolve_stored_procedure (PARSER_CONTEXT * parser, PT_NODE * node, PT_BIND_NAMES_ARG * bind_arg);

static PT_NODE *pt_find_entity_in_scopes (PARSER_CONTEXT * parser, SCOPES * scopes, UINTPTR spec_id);
static PT_NODE *pt_find_outer_entity_in_scopes (PARSER_CONTEXT * parser, SCOPES * scopes, UINTPTR spec_id,
                        short *scope_location);
static PT_NODE *pt_make_flat_list_from_data_types (PARSER_CONTEXT * parser, PT_NODE * res_list, PT_NODE * entity);
static PT_NODE *pt_undef_names_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *pt_undef_names_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static void fill_in_insert_default_function_arguments (PARSER_CONTEXT * parser, PT_NODE * const node);

static PT_NODE *pt_resolve_vclass_args (PARSER_CONTEXT * parser, PT_NODE * statement);
static int pt_function_name_is_spec_attr (PARSER_CONTEXT * parser, PT_NODE * name, PT_BIND_NAMES_ARG * bind_arg,
                      int *is_spec_attr);
static void pt_mark_function_index_expression (PARSER_CONTEXT * parser, PT_NODE * expr, PT_BIND_NAMES_ARG * bind_arg);
static void pt_bind_names_merge_insert (PARSER_CONTEXT * parser, PT_NODE * node, PT_BIND_NAMES_ARG * bind_arg,
                    SCOPES * scopestack, PT_EXTRA_SPECS_FRAME * specs_frame);
static void pt_bind_names_merge_update (PARSER_CONTEXT * parser, PT_NODE * node, PT_BIND_NAMES_ARG * bind_arg,
                    SCOPES * scopestack, PT_EXTRA_SPECS_FRAME * specs_frame);
static const char *pt_get_unique_exposed_name (PARSER_CONTEXT * parser, PT_NODE * first_spec);
static PT_NODE *pt_bind_name_to_spec (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);

static PT_NODE *pt_resolve_natural_join (PARSER_CONTEXT * parser, PT_NODE * node, void *chk_parent, int *continue_walk);

static void pt_resolve_natural_join_internal (PARSER_CONTEXT * parser, PT_NODE * join_lhs, PT_NODE * join_rhs);

static PT_NODE *pt_create_pt_expr_and_node (PARSER_CONTEXT * parser, PT_NODE * arg1, PT_NODE * arg2);

static PT_NODE *pt_create_pt_name (PARSER_CONTEXT * parser, PT_NODE * spec, NATURAL_JOIN_ATTR_INFO * attr);

static PT_NODE *pt_create_pt_expr_equal_node (PARSER_CONTEXT * parser, PT_NODE * arg1, PT_NODE * arg2);

static NATURAL_JOIN_ATTR_INFO *get_natural_join_attrs_from_pt_spec (PARSER_CONTEXT * parser, PT_NODE * node);

static bool natural_join_equal_attr (NATURAL_JOIN_ATTR_INFO * lhs, NATURAL_JOIN_ATTR_INFO * rhs);

static void free_natural_join_attrs (NATURAL_JOIN_ATTR_INFO * attrs);

static int generate_natural_join_attrs_from_subquery (PT_NODE * subquery_attrs_list, NATURAL_JOIN_ATTR_INFO ** attrs_p);

static int generate_natural_join_attrs_from_db_attrs (DB_ATTRIBUTE * db_attrs, NATURAL_JOIN_ATTR_INFO ** attrs_p);

static bool is_pt_name_in_group_having (PT_NODE * node);

static PT_NODE *pt_mark_pt_name (PARSER_CONTEXT * parser, PT_NODE * node, void *chk_parent, int *continue_walk);

static PT_NODE *pt_mark_group_having_pt_name (PARSER_CONTEXT * parser, PT_NODE * node, void *chk_parent,
                          int *continue_walk);

static void pt_resolve_group_having_alias_pt_sort_spec (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * select_list);

static void pt_resolve_group_having_alias_pt_name (PARSER_CONTEXT * parser, PT_NODE ** node_p, PT_NODE * select_list);

static void pt_resolve_group_having_alias_pt_expr (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * select_list);

static void pt_resolve_group_having_alias_internal (PARSER_CONTEXT * parser, PT_NODE ** node_p, PT_NODE * select_list);

static PT_NODE *pt_resolve_group_having_alias (PARSER_CONTEXT * parser, PT_NODE * node, void *chk_parent,
                           int *continue_walk);

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

static PT_NODE *pt_resolve_star_reserved_names (PARSER_CONTEXT * parser, PT_NODE * from);
static PT_NODE *pt_bind_reserved_name (PARSER_CONTEXT * parser, PT_NODE * in_node, PT_NODE * spec);
static PT_NODE *pt_set_reserved_name_key_type (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static void pt_bind_names_in_with_clause (PARSER_CONTEXT * parser, PT_NODE * node, PT_BIND_NAMES_ARG * bind_arg);
static void pt_bind_names_in_cte (PARSER_CONTEXT * parser, PT_NODE * node, PT_BIND_NAMES_ARG * bind_arg);
static PT_NODE *pt_bind_cte_self_references_types (PARSER_CONTEXT * parser, PT_NODE * node, void *arg,
                           int *continue_walk);
static PT_NODE *pt_get_attr_list_of_derived_table (PARSER_CONTEXT * parser, PT_MISC_TYPE derived_table_type,
                           PT_NODE * derived_table, PT_NODE * derived_name);
static void pt_set_attr_list_types (PARSER_CONTEXT * parser, PT_NODE * as_attr_list, PT_MISC_TYPE derived_table_type,
                    PT_NODE * derived_table, PT_NODE * parent_spec);
static PT_NODE *pt_count_with_clauses (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);

static int pt_resolve_dblink_server_name (PARSER_CONTEXT * parser, PT_NODE * node, char **server_owner_name);
static int pt_resolve_dblink_check_owner_name (PARSER_CONTEXT * parser, PT_NODE * node, char **server_owner_name);

static void pt_gather_dblink_colums (PARSER_CONTEXT * parser, PT_NODE * query_stmt);
typedef struct
{
  int norder;
  int name_pos;
  int type_idx;
  int dec_precision;
  int precision;
  int charset;
} S_REMOTE_COL_ATTR;

typedef struct remote_tbl_cols S_REMOTE_TBL_COLS;
struct remote_tbl_cols
{
private:
  int m_nm_alloc;
  int m_nm_used;
  char *m_nm_buf;

  int m_attr_alloc;
  int m_attr_used;
  bool m_attr_star;
  S_REMOTE_COL_ATTR *m_attr;

private:
    bool alloc_name_buffer (int len)
  {
    if (m_nm_alloc > (m_nm_used + len))
      {
    return true;
      }

    while (m_nm_alloc <= (m_nm_used + len))
      {
    m_nm_alloc += 1024;
      }

    m_nm_buf = (char *) (m_nm_buf ? realloc (m_nm_buf, m_nm_alloc) : malloc (m_nm_alloc));
    return (m_nm_buf != NULL);
  }

  bool alloc_attr_buffer ()
  {
    int base_size = 10;
    int incr_size = 2;

    if (m_attr == NULL || m_attr_alloc == 0)
      {
    m_attr_used = m_nm_used = 0;
    m_attr_alloc = base_size;
    m_attr = (S_REMOTE_COL_ATTR *) malloc (m_attr_alloc * sizeof (S_REMOTE_COL_ATTR));
      }
    else
      {
    if (m_attr_alloc <= m_attr_used)
      {
        m_attr = (S_REMOTE_COL_ATTR *) realloc (m_attr, sizeof (S_REMOTE_COL_ATTR) * (m_attr_used + incr_size));
        m_attr_alloc = m_attr_used + incr_size;
      }
      }

    return (m_attr != NULL);
  }

public:
  remote_tbl_cols ()
  {
    m_nm_alloc = m_nm_used = 0;
    m_nm_buf = NULL;
    m_attr_alloc = m_attr_used = 0;
    m_attr = NULL;
    m_attr_star = false;
  }

  ~remote_tbl_cols ()
  {
    if (m_nm_buf)
      {
    free (m_nm_buf);
      }
    if (m_attr)
      {
    free (m_attr);
      }
  }

  remote_tbl_cols (const remote_tbl_cols &) = delete;
  remote_tbl_cols & operator= (const remote_tbl_cols &) = delete;

  S_REMOTE_COL_ATTR *get_col_attr (char *name)
  {
    S_REMOTE_COL_ATTR *attr = NULL;
    int len;

    assert (name != NULL);
    if (alloc_attr_buffer ())
      {
    len = strlen (name) + 1 /* include '\0' */ ;
    if (alloc_name_buffer (len))
      {
        attr = &(m_attr[m_attr_used]);

        attr->norder = m_attr_used++;
        attr->name_pos = m_nm_used;

        memcpy (m_nm_buf + m_nm_used, name, len);
        m_nm_used += len;
      }
      }

    return attr;
  }

  const S_REMOTE_COL_ATTR *get_attr (int idx)
  {
    assert (idx >= 0 && idx < m_attr_used);
    return m_attr + idx;
  }

  const char *get_name (int idx)
  {
    assert (idx >= 0 && idx < m_attr_used);
    return m_nm_buf + m_attr[idx].name_pos;
  }

  int get_attr_size ()
  {
    return m_attr_used;
  }

  bool is_select_star ()
  {
    return m_attr_star;
  }

  void set_select_star ()
  {
    m_attr_star = true;
  }
};              /* struct remote_tbl_cols */

static int pt_remake_dblink_select_list (PARSER_CONTEXT * parser, PT_SPEC_INFO * class_spec,
                     S_REMOTE_TBL_COLS * rmt_cols);
static int pt_dblink_table_get_column_defs (PARSER_CONTEXT * parser, PT_NODE * dblink,
                        S_REMOTE_TBL_COLS * rmt_tbl_cols);

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

/*
 * pt_undef_names_pre () - Set error if name matching spec is found. Used in
 *             insert to make sure no "correlated" names are used
 *             in subqueries.
 *
 * return         : Unchanged node argument.
 * parser (in)        : Parser context.
 * node (in)          : Parse tree node.
 * arg (in)       : Insert spec.
 * continue_walk (in) : Continue walk.
 *
 * NOTE: Insert spec will store in etc the correlation level in regard with
 *   INSERT VALUE clause. Only if level is greater than 0, names should
 *   be undefined. INSERT INTO SET attr_i = EXPR (attr_j1, attr_j2, ...)
 *   is allowed.
 *   If etc is NULL, correlation level is ignored and all names are
 *   undefined.
 */
static PT_NODE *
pt_undef_names_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE *spec = (PT_NODE *) arg;
  short *level_p = NULL;

  if (spec == NULL)
    {
      *continue_walk = PT_STOP_WALK;
      return node;
    }

  level_p = (short *) spec->etc;

  switch (node->node_type)
    {
    case PT_NAME:
      if (level_p == NULL || *level_p > 0)
    {
      /* Using "correlated" names in INSERT VALUES clause is incorrect except when they are arguments of the
       * DEFAULT() function. */
      if (node->info.name.spec_id == spec->info.spec.id
          && !PT_NAME_INFO_IS_FLAGED (node, PT_NAME_INFO_FILL_DEFAULT))
        {
          int save_custom_print = parser->custom_print;
          parser->custom_print |= PT_SUPPRESS_RESOLVED;
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_DEFINED,
              pt_short_print (parser, node));
          parser->custom_print = save_custom_print;
        }
    }
      break;
    case PT_SELECT:
    case PT_UNION:
    case PT_INTERSECTION:
    case PT_DIFFERENCE:
    case PT_INSERT:
      if (level_p != NULL)
    {
      (*level_p)++;
    }
      break;
    default:
      break;
    }

  return node;
}

/*
 * pt_undef_names_post () - Function to be used with pt_undef_names_pre. Helps
 *              with counting the correlation level.
 *
 * return         : Unchanged node argument.
 * parser (in)        : Parser context.
 * node (in)          : Parse tree node.
 * arg (in)       : Insert spec.
 * continue_walk (in) : Continue walk.
 */
static PT_NODE *
pt_undef_names_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE *spec = (PT_NODE *) arg;
  short *level_p = NULL;

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

  level_p = (short *) spec->etc;
  if (level_p == NULL)
    {
      return node;
    }

  switch (node->node_type)
    {
    case PT_SELECT:
    case PT_UNION:
    case PT_INTERSECTION:
    case PT_DIFFERENCE:
    case PT_INSERT:
      (*level_p)--;
      break;
    default:
      break;
    }

  return node;
}

/*
 * pt_resolved() -  check if this path expr was previously resolved
 *   return:  true if expr was previously resolved
 *   expr(in): a path expression
 */

int
pt_resolved (const PT_NODE * expr)
{
  if (expr)
    {
      switch (expr->node_type)
    {
    case PT_NAME:
      return (expr->info.name.spec_id != 0);
    case PT_DOT_:
      return (pt_resolved (expr->info.dot.arg1) && pt_resolved (expr->info.dot.arg2));
    case PT_FUNCTION:
      // Resolved as a function node.
      // If it's actually a user-defined function, this node will be resolved in the next phase (function resolution).
      return (expr->info.function.function_type == PT_GENERIC);
    default:
      break;
    }
    }
  return 0;
}


/*
 * pt_eval_value_path() -
 *   return:  pt_value node if successful, NULL otherwise
 *   parser(in): the parser context
 *   path(in): a path expression anchored by a PT_VALUE node
 */

PT_NODE *
pt_eval_value_path (PARSER_CONTEXT * parser, PT_NODE * path)
{
  DB_VALUE val;
  PT_NODE *tmp;

  db_make_null (&val);

  if (pt_eval_path_expr (parser, path, &val))
    {
      /* make val into a PT_VALUE node */
      tmp = pt_dbval_to_value (parser, &val);
      if (tmp)
    {
      tmp->line_number = path->line_number;
      tmp->column_number = path->column_number;
      /* NOTE node IS NO LONGER TYPE PT_NAME! */
      pr_clear_value (&val);
    }
      return tmp;
    }
  else
    {
      return NULL;      /* error set by pt_eval_path_expr */
    }
}               /* pt_eval_value_path */

/*
 * pt_bind_param_node() -  try to bind parameter for a node
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in):
 */
PT_NODE *
pt_bind_param_node (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  if (node->node_type == PT_NAME && node->info.name.meta_class == PT_PARAMETER)
    {
      return pt_bind_parameter (parser, node);
    }
  return NULL;
}

/*
 * pt_bind_parameter() -  try to resolve name as an interpreter
 *                        parameter reference
 *   return:  lbl's value if successful, NULL otherwise
 *   parser(in): the parser context
 *   parameter(in/out): a PT_NAME node
 */
static PT_NODE *
pt_bind_parameter (PARSER_CONTEXT * parser, PT_NODE * parameter)
{
  PT_NODE *node = NULL;
  const char *name;
  DB_VALUE *db_val;

  if (!parameter || parameter->node_type != PT_NAME)
    {
      return NULL;      /* nothing doing */
    }

  if (parameter->type_enum != PT_TYPE_NONE)
    {
      return parameter;     /* is already resolved */
    }

  /* look up as an interpreter parameter */
  name = parameter->info.name.original;
  db_val = pt_find_value_of_label (name);
  if (db_val)           /* parameter found, resolve to its matching DB_VALUE */
    {
      /* create a PT_VALUE image of the matching DB_VALUE */

      /* If type is object type, the referenced object may not exist. We need to first check its existence, then we
       * can resolve the object.
       */
      if (DB_VALUE_TYPE (db_val) == DB_TYPE_OBJECT)
    {
      MOP mop = db_get_object (db_val);
      int does_exist = locator_does_exist_object (mop, DB_FETCH_READ);
      if (does_exist == LC_ERROR || does_exist == LC_DOESNOT_EXIST)
        {
          assert (does_exist == LC_DOESNOT_EXIST || er_errid () != NO_ERROR);
          return NULL;
        }
      assert (does_exist == LC_EXIST);
      /* Fall through. */
    }
      node = pt_dbval_to_value (parser, db_val);
      if (node == NULL)
    {
      return NULL;
    }

      /* NOTE node IS NO LONGER TYPE PT_NAME! */
      parameter->type_enum = node->type_enum;
      if (parameter->data_type)
    {
      parser_free_tree (parser, parameter->data_type);
    }
      parameter->data_type = node->data_type;
      node->data_type = NULL;
      parser_free_tree (parser, node);
      parameter->info.name.meta_class = PT_PARAMETER;
    }

  if (parameter->info.name.meta_class == PT_PARAMETER)
    {
      return parameter;
    }
  else
    {
      return NULL;
    }
}

/*
 * pt_bind_parameter_path() -  try to resolve name or path as an interpreter
 *      parameter reference or a path expression anchored by a path expression.
 *   return:  path's value (evaluated) if successful, NULL otherwise
 *   parser(in): the parser context
 *   path(in/out): a PT_NAME or PT_DOT_ node
 */
static PT_NODE *
pt_bind_parameter_path (PARSER_CONTEXT * parser, PT_NODE * path)
{
  PT_NODE *arg1 = NULL;
  PT_NODE *temp;

  if (!path)
    {
      return NULL;      /* nothing doing */
    }

  if (path->node_type == PT_NAME)
    {
      path = pt_bind_parameter (parser, path);
      /* parameter paths must start with a parameter. */
      if (!path || path->info.name.meta_class != PT_PARAMETER)
    {
      return NULL;
    }
      return path;
    }

  if (path->node_type == PT_DOT_)
    {
      arg1 = pt_bind_parameter_path (parser, path->info.dot.arg1);
      if (!arg1)
    {
      return NULL;
    }
      /* If we succesfully resolved the parameter, mark the right hand side as parameter too. This will be evaluated at
       * run time. */
      path->info.dot.arg1 = arg1;
      path->info.dot.arg2->info.name.meta_class = PT_PARAMETER;

      /* We need to be able to evaluate it now, in order to get the type of the expression. */
      temp = pt_eval_value_path (parser, path);
      if (temp)
    {
      path->type_enum = temp->type_enum;
      if (path->data_type)
        {
          parser_free_tree (parser, path->data_type);
        }
      path->data_type = temp->data_type;
      temp->data_type = NULL;
      parser_free_tree (parser, temp);

      return path;
    }
    }

  return NULL;
}               /* pt_bind_parameter_path */

/*
 * pt_bind_reserved_name () - Try to resolve name to one of the reserved names
 *
 * return          : Resolved reserved name or NULL.
 * parser (in)         : Parser context.
 * in_node (in)        : Original name node.
 * spec (in)           : The spec to which the reserved name will belong.
 *
 * NOTE: Reserved names are allowed only if used in the context of a SELECT
 *   statement on a single table and having certain hints that unlock
 *   reserved names.
 */
static PT_NODE *
pt_bind_reserved_name (PARSER_CONTEXT * parser, PT_NODE * in_node, PT_NODE * spec)
{
  int i = 0;
  const char *name = NULL;
  PT_NODE *reserved_name = NULL;

  assert (in_node != NULL && spec != NULL);

  /* get attribute name */
  if (in_node->node_type == PT_NAME)
    {
      name = in_node->info.name.original;
    }
  else if (in_node->node_type == PT_DOT_)
    {
      /* we can only allow X.reserved_name where X is the name of spec */
      if (in_node->info.dot.arg1->node_type != PT_NAME
      || pt_str_compare (in_node->info.dot.arg1->info.name.original, spec->info.spec.range_var->info.name.original,
                 CASE_INSENSITIVE))
    {
      return NULL;
    }
      if (in_node->info.dot.arg2->node_type != PT_NAME)
    {
      return NULL;
    }
      name = in_node->info.dot.arg2->info.name.original;
    }
  else
    {
      /* not the scope of this function */
      return NULL;
    }

  /* look for the name in reserved name table */
  for (i = 0; i < RESERVED_ATTR_COUNT; i++)
    {
      if (!pt_str_compare (name, pt_Reserved_name_table[i].name, CASE_INSENSITIVE))
    {
      /* the found reserved name should match the type of scan to which the spec is flagged... otherwise it is a
       * wrong name */
      if (!PT_CHECK_RESERVED_NAME_BIND (spec, i))
        {
          /* Unknown reserved name in current context */
          return NULL;
        }

      /* bind the reserved name */
      if (in_node->node_type == PT_NAME)
        {
          reserved_name = in_node;
        }
      else          /* PT_DOT_ */
        {
          reserved_name = in_node->info.dot.arg2;
          in_node->info.dot.arg2 = NULL;
          PT_NODE_MOVE_NUMBER_OUTERLINK (reserved_name, in_node);
          parser_free_tree (parser, in_node);
        }
      reserved_name->info.name.spec_id = spec->info.spec.id;
      reserved_name->info.name.resolved = spec->info.spec.range_var->info.name.original;
      reserved_name->info.name.meta_class = PT_RESERVED;
      reserved_name->info.name.reserved_id = (PT_RESERVED_NAME_ID) i;
      reserved_name->type_enum = pt_db_to_type_enum (pt_Reserved_name_table[i].type);
      if (reserved_name->type_enum == PT_TYPE_OBJECT)
        {
          reserved_name->data_type =
        pt_domain_to_data_type (parser,
                    tp_domain_resolve (pt_Reserved_name_table[i].type,
                               spec->info.spec.entity_name->info.name.db_object, 0, 0, NULL,
                               0));
        }
      return reserved_name;
    }
    }
  /* this is not a reserved name */
  return NULL;
}

/*
 * pt_bind_name_or_path_in_scope() - tries to resolve in_node using all the
 *     entity_spec_lists in scopes and returns in_node if successfully resolved
 *   return:  in_node's resolution if successful, NULL if in_node is unresolved
 *   parser(in): the parser context
 *   bind_arg(in): a list of scopes for resolving names & path expressions
 *   in_node(in): an attribute reference or path expression to be resolved
 *
 * Note :
 * Unfortunately, we can't push the check for naked parameters
 * into pt_get_resolution() because parameters that have the
 * name as an attribute of some (possibly enclosing) scope must be
 * to the attribute and not the parameter (by our convention).
 * when naked parameters are eliminated, this mickey mouse stuff
 * will go away...
 */
static PT_NODE *
pt_bind_name_or_path_in_scope (PARSER_CONTEXT * parser, PT_BIND_NAMES_ARG * bind_arg, PT_NODE * in_node)
{
  PT_NODE *prev_entity = NULL;
  PT_NODE *node = NULL;
  SCOPES *scopes = bind_arg->scopes;
  SCOPES *scope;
  int level = 0;
  PT_NODE *temp, *entity;
  short scope_location;
  bool error_saved = false;

  /* skip hint argument name, index name */
  if (in_node->node_type == PT_NAME
      && (in_node->info.name.meta_class == PT_HINT_NAME || in_node->info.name.meta_class == PT_INDEX_NAME))
    {
      return in_node;
    }

  /* skip resolved nodes */
  if (pt_resolved (in_node))
    {
      return in_node;
    }

  if (er_errid () != NO_ERROR)
    {
      /*
         in case that the dblink server is not found
         it is meaningless to call pt_get_resoultion.
       */
      if (er_errid () == ER_DBLINK_SERVER_NOT_FOUND)
    {
      return in_node;
    }

      er_stack_push ();
      error_saved = true;
    }

  /* resolve all name nodes and path expressions */
  if (scopes)
    {
      for (scope = scopes; scope != NULL; scope = scope->next)
    {
      node = pt_get_resolution (parser, bind_arg, scope->specs, in_node, &prev_entity, 1);
      if (node)
        {
          node = pt_expand_external_path (parser, node, &prev_entity);
          break;
        }
      if (is_pt_name_in_group_having (in_node))
        {
          /* give up; this kind of name should be in level 0 scopes */
          break;
        }
      level++;
    }
      if (node && er_errid () == ER_OBJ_INVALID_ATTRIBUTE)
    {
      /* An error is meaningful only when it cannot be found in all scopes. */
      er_clear ();
    }
    }
  else
    {
      /* resolve class attributes and anything else that can be resolved without an enclosing scope. */
      node = pt_get_resolution (parser, bind_arg, NULL, in_node, &prev_entity, 1);

      if (node)
    {
      node = pt_expand_external_path (parser, node, &prev_entity);
    }
    }

  if (node)
    {
      /* set the correlation of either the name or arg2 of the path */
      /* expression. */
      if (node->node_type == PT_DOT_)
    {
      node->info.dot.arg2->info.name.correlation_level = level;
    }
      else
    {
      node->info.name.correlation_level = level;
    }

      /* all is well, name is resolved */
      /* check correlation level of scope */
      scope = scopes;
      while (level > 0 && scope)
    {
      /* it was correlated. Choose the closest correlation scope. That is the same as choosing the smallest
       * non-zero number. */
      if (!scope->correlation_level || scope->correlation_level > (unsigned short) level)
        {
          scope->correlation_level = level;
        }
      level = level - 1;
      scope = scope->next;
    }
    }
  else
    {
      /* If pt_name in group by/ having, maybe it's alias. We will try to resolve it later. */
      if (!is_pt_name_in_group_having (in_node))
    {
      if (parser->flag.is_parsing_static_sql == 1
          && ((in_node->node_type == PT_DOT_ && !pt_resolved (in_node->info.dot.arg2))
          || in_node->node_type == PT_NAME))
        {
          // clear unknown attribute error, the unknown symbol will be converted (paramterized) to host variable
          pt_reset_error (parser);
          if (er_errid () == ER_OBJ_INVALID_ATTRIBUTE)
        {
          er_clear ();
        }

          node = pt_parameterize_for_static_sql (parser, in_node);
        }
      else          // in case of compiling static SQL, do not resolve with variable defined in runtime
        {
          /* it may be a naked parameter or a path expression anchored by a naked parameter. Try and resolve it as
           * such. */
          node = pt_bind_parameter_path (parser, in_node);
        }

      if (node == NULL && !pt_has_error (parser))
        {
          if (er_errid () != NO_ERROR)
        {
          PT_ERRORc (parser, in_node, er_msg ());
        }
          else
        {
          PT_ERRORmf (parser, in_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_DEFINED,
                  pt_short_print (parser, in_node));
        }
        }
    }
    }

  if (node != NULL)
    {
      /* outer join restriction check */
      for (temp = node; temp->node_type == PT_DOT_; temp = temp->info.dot.arg2)
    {
      ;
    }
      if (temp->node_type == PT_NAME && temp->info.name.location > 0)
    {
      /* PT_NAME node within outer join condition */
      if (temp != node)
        {
          /* node->node_type is PT_DOT_; that menas path expression */
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUTERJOIN_PATH_EXPR,
              pt_short_print (parser, node));
          node = NULL;
        }
      else
        {
          /* check scope */
          scope_location = temp->info.name.location;
          entity = pt_find_outer_entity_in_scopes (parser, scopes, temp->info.name.spec_id, &scope_location);
          if (!entity)
        {
          /* cannot resolve within the outer join scope */
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUTERJOIN_SCOPE,
                  pt_short_print (parser, node));
          node = NULL;
        }
          else if (entity->info.spec.location < 0 || (entity->info.spec.location > temp->info.name.location)
               || scope_location > entity->info.spec.location)
        {
          /* cannot resolve within the outer join scope */
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUTERJOIN_SCOPE,
                  pt_short_print (parser, node));
          node = NULL;
        }
        }
    }
    }

  if (error_saved)
    {
      er_stack_pop ();
    }

  return node;
}


/*
 * pt_bind_type_of_host_var() -  set the type of a host variable to
 *                               the type of its DB_VALUE
 *   return:  none
 *   parser(in/out): the parser context
 *   hv(in/out): an input host variable
 */
static void
pt_bind_type_of_host_var (PARSER_CONTEXT * parser, PT_NODE * hv)
{
  DB_VALUE *val = NULL;

  val = pt_host_var_db_value (parser, hv);
  if (val)
    {
      hv = pt_bind_type_from_dbval (parser, hv, val);
    }
  /* else : There isn't a host var yet.  This happens if someone does a db_compile_statement before doing
   * db_push_values, as might happen in a dynamic esql PREPARE statement where the host vars might not be supplied
   * until some later EXECUTE or OPEN CURSOR statement. In this case, we'll have to rely on pt_coerce_value and
   * pt_value_to_dbval to fix things up later. */
}

/*
 * pt_bind_spec_attrs() -  bind name types for a derived table.
 *   return:  void
 *   parser(in/out): the parser context
 *   spec(in/out): an entity spec describing a derived table
 *
 * Note :
 * if spec.derived_table_type is set expr then assert: spec.derived_table.type
 * is a set type in any case, check that:
 *   the number of derived columns in spec.as_attr_list matches
 *   the number of attributes in spec.derived_table.select_list
 * foreach column c in spec.as_attr_list and foreach attribute a
 * in spec.derived_table.select_list do make c assume a's datatype and
 * tag it with spec's id
 */
static void
pt_bind_spec_attrs (PARSER_CONTEXT * parser, PT_NODE * spec)
{
  PT_NODE *derived_table = NULL, *col, *attr, *cte = NULL;
  PT_SPEC_INFO *spec_info;

  if (!parser || !spec || spec->node_type != PT_SPEC || PT_SPEC_IS_ENTITY (spec))
    {
      return;
    }

  spec_info = &spec->info.spec;

  /* get aliases if they are not present */
  if (PT_SPEC_IS_DERIVED (spec))
    {
      derived_table = spec_info->derived_table;
      if (spec_info->as_attr_list == NULL)
    {
      spec_info->as_attr_list =
        pt_get_attr_list_of_derived_table (parser, spec_info->derived_table_type, derived_table,
                           spec_info->range_var);
    }

      /* get types from derived table; */
      pt_set_attr_list_types (parser, spec_info->as_attr_list, spec_info->derived_table_type, derived_table, spec);
    }
  else
    {
      assert (PT_SPEC_IS_CTE (spec));
      cte = spec_info->cte_pointer->info.pointer.node;

      /* copy cte as_attr_list to spec, to be used for binding */
      if (spec_info->as_attr_list == NULL)
    {
      if (cte->info.cte.as_attr_list == NULL)
        {
          /* as_attr_list of cte should be previously computed in pt_bind_names_in_cte */
          PT_INTERNAL_ERROR (parser, "cte definition error");
          return;
        }
      spec_info->as_attr_list = parser_copy_tree_list (parser, cte->info.cte.as_attr_list);
    }
      else
    {
      /* copy cte types from its as_attr_list */
      if (pt_length_of_list (spec_info->as_attr_list) != pt_length_of_list (cte->info.cte.as_attr_list))
        {
          PT_ERRORmf3 (parser, spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ATT_CNT_NE_DERIVED_C,
               pt_short_print (parser, spec), pt_length_of_list (cte->info.cte.as_attr_list),
               pt_length_of_list (spec_info->as_attr_list));
        }

      col = spec_info->as_attr_list;
      attr = cte->info.cte.as_attr_list;
      for (; col != NULL && attr != NULL; col = col->next, attr = attr->next)
        {
          col->type_enum = attr->type_enum;
          if (attr->data_type)
        {
          col->data_type = parser_copy_tree_list (parser, attr->data_type);
        }
          else
        {
          parser_free_tree (parser, col->data_type);
          col->data_type = NULL;
        }
        }
      assert (col == NULL && attr == NULL);
    }
    }

  for (col = spec->info.spec.as_attr_list; col != NULL; col = col->next)
    {
      /* tag it as resolved */
      col->info.name.spec_id = spec->info.spec.id;
      col->info.name.meta_class = PT_NORMAL;
    }
}

int
pt_resolve_server_names (PARSER_CONTEXT * parser, PT_NODE * spec)
{
  int ret = NO_ERROR;

  PT_NODE *table = spec->info.spec.remote_server_name;
  PT_DBLINK_INFO *dblink_table = &table->info.dblink_table;

  assert (dblink_table->is_name);

  /*
   ** dblink_table, others : owner_list
   ** ----------------------------------
   **   tbl                :    NULL
   **   user.tbl           :    NULL
   **   user.tbl, user.tbl :    NULL
   **   tbl,      tbl      :    NULL
   **   user.tbl, tbl      :   "user"
   **   tbl,      user.tbl :   "user"
   */

  if (dblink_table->owner_list == NULL)
    {
      return pt_resolve_dblink_server_name (parser, table, NULL);
    }

  char *server_owner_ref = NULL;
  if (table->info.dblink_table.owner_name)
    {
      PT_NODE *tmp = table->info.dblink_table.owner_name;
      table->info.dblink_table.owner_name = NULL;
      ret = pt_resolve_dblink_server_name (parser, table, &server_owner_ref);
      table->info.dblink_table.owner_name = tmp;
    }
  else
    {
      ret = pt_resolve_dblink_server_name (parser, table, &server_owner_ref);
    }

  return ret;
}

/*
 * pt_bind_scope() -  bind names and types of derived tables in current scope.
 *   return:  void
 *   parser(in): the parser context
 *   bind_arg(in/out): a list of scopes with the current scope on "top"
 *
 * Note :
 * this definition of a derived table's scope allows us to resolve the 3rd f in
 *   select f.ssn from faculty1 f, (select ssn from faculty g where f=g) h(ssn)
 * and still catch the 1st illegal forward reference to f in
 *   select n from (select ssn from faculty g where f=g) h(n), faculty f
 */
static void
pt_bind_scope (PARSER_CONTEXT * parser, PT_BIND_NAMES_ARG * bind_arg)
{
  SCOPES *scopes = bind_arg->scopes;
  PT_NODE *spec, *prev_spec = NULL;
  bool save_donot_fold;

  /* for dblink: remote table's columns */
  S_REMOTE_TBL_COLS *rmt_tbl_cols;

  spec = scopes->specs;
  scopes->specs = NULL;
  while (spec)
    {

      if (PT_SPEC_IS_DERIVED (spec))
    {
      /* evaluate the names of the current table. The name scope of the first spec, is only the outer level scopes. The
       * outer scopes are pointed to by scopes->next. The null "scopes" spec is kept to maintain correlation level
       * calculation.
       */
      PT_NODE *table;

      assert (!PT_SPEC_IS_ENTITY (spec) && !PT_SPEC_IS_CTE (spec));
      table = spec->info.spec.derived_table;
      if (table->node_type == PT_JSON_TABLE)
        {
          assert (spec->info.spec.derived_table_type == PT_DERIVED_JSON_TABLE);
          table->info.json_table_info.expr =
        parser_walk_tree (parser, table->info.json_table_info.expr, pt_bind_names, bind_arg, pt_bind_names_post,
                  bind_arg);
          table->info.json_table_info.tree =
        parser_walk_tree (parser, table->info.json_table_info.tree, pt_bind_name_to_spec, spec, NULL, NULL);
        }
      else if (table->node_type == PT_DBLINK_TABLE)
        {
          assert (spec->info.spec.derived_table_type == PT_DERIVED_DBLINK_TABLE);

          int err;
          REMOTE_COLS *remote;
          PT_DBLINK_INFO *dblink_table = &table->info.dblink_table;

          if (table->info.dblink_table.is_name && table->info.dblink_table.url == NULL)
        {
          if (pt_resolve_dblink_server_name (parser, table, NULL) != NO_ERROR)
            {
              return;
            }
        }

          /* remote table's column list */
          rmt_tbl_cols = new (std::nothrow) S_REMOTE_TBL_COLS;
          if (rmt_tbl_cols == NULL)
        {
          PT_ERRORf (parser, table, "Failed to get column information for memory allocation error", ER_DBLINK);
          return;
        }

          err = pt_dblink_table_get_column_defs (parser, table, rmt_tbl_cols);

          if (table->info.dblink_table.remote_table_name && *table->info.dblink_table.remote_table_name)
        {
          /* error from table-name@server */
          if (err < 0)
            {
              goto error_exit;
            }

          if (table->info.dblink_table.cols == NULL)
            {
              if (pt_remake_dblink_select_list (parser, &spec->info.spec, rmt_tbl_cols) != NO_ERROR)
            {
              if (!pt_has_error (parser))
                {
                  PT_ERROR (parser, table, "Failed to make select-list for dblink");
                }
              goto error_exit;
            }
            }
        }

          /* error from dblink(server, 'select ... from ...' */
          if (err < 0)
        {
          goto error_exit;
        }

          dblink_table->remote_col_list = (void *) rmt_tbl_cols;

          /* save the column lists to free at end of parsing */
          remote = (REMOTE_COLS *) parser_alloc (parser, sizeof (REMOTE_COLS));
          if (remote == NULL)
        {
          PT_INTERNAL_ERROR (parser, "parser_alloc");
          goto error_exit;
        }

          /* add dblink remote table's column list to Parser */
          remote->cols = (void *) rmt_tbl_cols;
          remote->next = parser->dblink_remote;
          parser->dblink_remote = remote;

          table->info.dblink_table.cols =
        parser_walk_tree (parser, table->info.dblink_table.cols, pt_bind_name_to_spec, spec, NULL, NULL);
        }
      else
        {
          table = parser_walk_tree (parser, table, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);
        }
      spec->info.spec.derived_table = table;

      /* must bind any expr types in table. pt_bind_types requires it. */
      save_donot_fold = bind_arg->sc_info->donot_fold;  /* save */
      bind_arg->sc_info->donot_fold = true; /* skip folding */
      table = pt_semantic_type (parser, table, bind_arg->sc_info);
      bind_arg->sc_info->donot_fold = save_donot_fold;  /* restore */
      if (table != NULL)
        {
          spec->info.spec.derived_table = table;
          /* table spec passed bind_names and semantic_type */
          pt_bind_spec_attrs (parser, spec);
        }
      else
        {
          assert (pt_has_error (parser));
        }
    }
      else if (PT_SPEC_IS_CTE (spec))
    {
      /* types and names of CTE should be already evaluated by pt_bind_names_in_cte; bind them to spec */
      pt_bind_spec_attrs (parser, spec);
    }
      else if (spec->info.spec.remote_server_name &&
           (spec->info.spec.remote_server_name->node_type == PT_DBLINK_TABLE_DML))
    {
      if (pt_resolve_server_names (parser, spec) != NO_ERROR)
        {
          return;
        }
    }

      if (prev_spec)
    {
      prev_spec->next = spec;
    }
      else
    {
      scopes->specs = spec;
    }
      prev_spec = spec;
      spec = prev_spec->next;
      prev_spec->next = NULL;
    }

  return;

error_exit:
  if (rmt_tbl_cols)
    {
      delete rmt_tbl_cols;
    }
}

/*
 * pt_find_function_type () - function name to look up
 *   return: function_type, or generic if not found
 *   name(in):
 */
static FUNC_CODE
pt_find_function_type (const char *name)
{
  if (name)
    {
      if (intl_mbs_casecmp (name, "set") == 0)
    {
      return F_TABLE_SET;
    }
      else if (intl_mbs_casecmp (name, "multiset") == 0)
    {
      return F_TABLE_MULTISET;
    }
      else if (intl_mbs_casecmp (name, "sequence") == 0)
    {
      return F_TABLE_SEQUENCE;
    }
    }
  return PT_GENERIC;
}


/*
 * pt_mark_location () -
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
pt_mark_location (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  short *location = (short *) arg;

  switch (node->node_type)
    {
    case PT_EXPR:
      node->info.expr.location = *location;
      break;
    case PT_NAME:
      node->info.name.location = *location;
      break;
    case PT_VALUE:
      node->info.value.location = *location;
      break;
    default:
      break;
    }

  return node;
}               /* pt_mark_location() */

/*
 * pt_set_is_view_spec () -
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in):
 */
PT_NODE *
pt_set_is_view_spec (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  bool do_not_replace_orderby = (bool *) arg ? *((bool *) arg) : false;
  if (!node)
    {
      return node;
    }

  if (pt_is_query (node))
    {
      /* Reset query id # */
      node->info.query.id = (UINTPTR) node;
      node->info.query.is_view_spec = 1;

      if (do_not_replace_orderby)
    {
      node->flag.do_not_replace_orderby = 1;
    }
    }

  return node;
}               /* pt_set_is_view_spec */

/*
 * pt_bind_names_post() -  bind names & path expressions of this statement node
 *   return:  node
 *   parser(in): the parser context
 *   node(in): a parse tree node
 *   arg(in): a list of scopes for resolving names & path expressions
 *   continue_walk(in): flag that tells when to stop tree traversal
 */
static PT_NODE *
pt_bind_names_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_BIND_NAMES_ARG *bind_arg = (PT_BIND_NAMES_ARG *) arg;

  *continue_walk = PT_CONTINUE_WALK;

  if (!node)
    {
      return node;
    }

  switch (node->node_type)
    {
    case PT_UPDATE:
    case PT_MERGE:
      {
    PT_NODE *temp, *lhs, *assignments = NULL, *spec = NULL;
    int error = NO_ERROR;

    if (node->node_type == PT_UPDATE)
      {
        assignments = node->info.update.assignment;
        spec = node->info.update.spec;
      }
    else if (node->node_type == PT_MERGE)
      {
        assignments = node->info.merge.update.assignment;
        assert (node->info.merge.into->next == NULL);
        node->info.merge.into->next = node->info.merge.using_clause;
        spec = node->info.merge.into;
      }

    /* this is only to eliminate oid names from lhs of assignments per ANSI. This is because name resolution for
     * OID's conflicts with ANSI. */
    for (temp = assignments; temp && error == NO_ERROR; temp = temp->next)
      {
        lhs = temp->info.expr.arg1;
        if (PT_IS_N_COLUMN_UPDATE_EXPR (lhs))
          {
        lhs = lhs->info.expr.arg1;
          }

        for (; lhs && error == NO_ERROR; lhs = lhs->next)
          {
        if (lhs->node_type == PT_NAME)
          {
            /* make it print like ANSI */
            if (lhs->info.name.meta_class == PT_OID_ATTR)
              {
            /* must re-resolve the name */
            lhs->info.name.original = lhs->info.name.resolved;
            lhs->info.name.spec_id = 0;
            if (!pt_find_name_in_spec (parser, spec, lhs))
              {
                error = MSGCAT_SEMANTIC_IS_NOT_DEFINED;
                PT_ERRORmf (parser, lhs, MSGCAT_SET_PARSER_SEMANTIC, error, pt_short_print (parser, lhs));
              }
              }
          }
          }
      }
    if (node->node_type == PT_MERGE)
      {
        node->info.merge.into->next = NULL;
      }
      }

      break;

    case PT_VALUE:
      {
    PT_NODE *elem;
    int is_function = 0;

    if (node->type_enum == PT_TYPE_STAR)
      {
        break;
      }

    if (node && pt_is_set_type (node))
      {
        node = pt_semantic_type (parser, node, bind_arg->sc_info);
        if (node == NULL)
          {
        return node;
          }

        elem = node->info.value.data_value.set;
        while (elem)
          {
        if (elem->node_type != PT_VALUE)
          {
            is_function = 1;
            break;
          }
        elem = elem->next;
          }
      }

    if (!is_function)
      {
        if (pt_value_to_db (parser, node) == NULL)
          {
        parser_free_tree (parser, node);
        node = NULL;
          }
        else
          {
        /*
         * pt_value_to_db has already filled the contents
         * of node->info.value.db_value; we don't need to
         * repeat that work here.
         * The type info from "node" drove that process, and
         * is assumed to be good.  If there was any touch-up
         * work required (e.g., adding a data_type node),
         * pt_value_to_db took care of it.
         */
          }
      }
    else
      {
        PT_NODE *arg_list = node->info.value.data_value.set;
        /* roll back error messages for set values. this is a set function reference, which we just realized, since
         * the syntax for constant sets and set functions is the same. Convert the node to a set function. */
        node->node_type = PT_FUNCTION;
        /* make info set up properly */
        memset (&(node->info), 0, sizeof (node->info));
        node->info.function.arg_list = arg_list;
        if (node->type_enum == PT_TYPE_SET)
          {
        node->info.function.function_type = F_SET;
          }
        else if (node->type_enum == PT_TYPE_MULTISET)
          {
        node->info.function.function_type = F_MULTISET;
          }
        else if (node->type_enum == PT_TYPE_SEQUENCE)
          {
        node->info.function.function_type = F_SEQUENCE;
          }
        else
          {
        node->info.function.function_type = (FUNC_CODE) 0;
          }

        /* now we need to type the innards of the set ... */
        /* first we tag this not typed so the type will be recomputed from scratch. */
        node->type_enum = PT_TYPE_NONE;
        node = pt_semantic_type (parser, node, bind_arg->sc_info);
      }

      }
      break;

    case PT_EXPR:
      (void) pt_instnum_compatibility (node);
      pt_mark_function_index_expression (parser, node, bind_arg);
      break;

    case PT_SELECT:
      if (node->info.query.q.select.from != NULL && PT_SPEC_SPECIAL_INDEX_SCAN (node->info.query.q.select.from))
    {
      /* This is a hack to determine type for index key attributes which may be different index. Obtain index info
       * and update type_enum and data type for all references to index keys. */
      PT_NODE *spec = node->info.query.q.select.from;
      DB_OBJECT *obj = spec->info.spec.entity_name->info.name.db_object;
      PT_NODE *index = node->info.query.q.select.using_index;
      SM_CLASS *class_ = NULL;
      SM_CLASS_CONSTRAINT *cons = NULL;
      TP_DOMAIN *key_domain;
      PT_BIND_NAMES_DATA_TYPE key_type;

      /* Get class object */
      if (au_fetch_class_force (obj, &class_, AU_FETCH_READ) != NO_ERROR)
        {
          PT_INTERNAL_ERROR (parser, "Error obtaining SM_CLASS");
          parser_free_tree (parser, node);
          return NULL;
        }
      /* Get index */
      cons = classobj_find_class_index (class_, index->info.name.original);
      if (cons == NULL)
        {
          PT_INTERNAL_ERROR (parser, "Hint argument should be the name of a" "valid index");
          parser_free_tree (parser, node);
          return NULL;
        }
      /* Get key type for index */
      if (btree_get_index_key_type (cons->index_btid, &key_domain) != NO_ERROR)
        {
          PT_INTERNAL_ERROR (parser, "Error obtaining index key type");
          parser_free_tree (parser, node);
          return NULL;
        }

      if (key_domain == NULL)
        {
          /* do nothing */
          return node;
        }

      /* Generate one data_type sample */
      key_type.type_enum = pt_db_to_type_enum (TP_DOMAIN_TYPE (key_domain));
      key_type.data_type = pt_domain_to_data_type (parser, key_domain);
      if (key_type.data_type == NULL)
        {
          PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          parser_free_tree (parser, node);
          return NULL;
        }

      /* Walk parse tree and change type enum for all RESERVED_KEY_KEY name nodes. */
      node = parser_walk_tree (parser, node, pt_set_reserved_name_key_type, &key_type, NULL, NULL);

      parser_free_tree (parser, key_type.data_type);
    }
      break;

    default:
      break;
    }

  return node;
}

/*
 * pt_check_Oracle_outerjoin () -
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_check_Oracle_outerjoin (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_BIND_NAMES_ARG *bind_arg = (PT_BIND_NAMES_ARG *) arg;
  SEMANTIC_CHK_INFO *sc_info;

  if (!node)
    {
      return node;
    }

  switch (node->node_type)
    {
    case PT_NAME:
      sc_info = bind_arg->sc_info;

      /* found node which is bound to the specified spec */
      if (node->node_type == PT_NAME && (node->info.name.spec_id == sc_info->Oracle_outerjoin_spec->info.spec.id))
    {
      sc_info->Oracle_outerjoin_attr_num++;
    }

      /* don't revisit leaves */
      *continue_walk = PT_STOP_WALK;
      break;

    case PT_SELECT:
      sc_info = bind_arg->sc_info;

      /* check for subquery in ON cond */
      sc_info->Oracle_outerjoin_subq_num++;
      break;

    case PT_DOT_:
      sc_info = bind_arg->sc_info;

      /* check for path expression in ON cond */
      sc_info->Oracle_outerjoin_path_num++;
      break;

    default:
      break;
    }

  return node;
}

/*
 * pt_clear_Oracle_outerjoin_spec_id () -
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_clear_Oracle_outerjoin_spec_id (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_BIND_NAMES_ARG *bind_arg = (PT_BIND_NAMES_ARG *) arg;
  SEMANTIC_CHK_INFO *sc_info;

  if (!node)
    {
      return node;
    }

  switch (node->node_type)
    {
    case PT_NAME:
      sc_info = bind_arg->sc_info;

      /* found node which is bound to the specified spec */
      if (node->node_type == PT_NAME)
    {
      /* clear spec_id to re-check outer join semantic */
      node->info.name.spec_id = 0;
    }

      /* don't revisit leaves */
      *continue_walk = PT_STOP_WALK;
      break;

    default:
      break;
    }

  return node;
}

void
pt_set_fill_default_in_path_expression (PT_NODE * node)
{
  if (node == NULL)
    {
      return;
    }
  if (node->node_type == PT_DOT_)
    {
      pt_set_fill_default_in_path_expression (node->info.dot.arg1);
      pt_set_fill_default_in_path_expression (node->info.dot.arg2);
    }
  else if (node->node_type == PT_NAME)
    {
      PT_NAME_INFO_SET_FLAG (node, PT_NAME_INFO_FILL_DEFAULT);

      /* We also need to clear the spec id because this PT_NAME node might be a copy of a node that has been resolved
       * without filling in the default value. The parser_copy_tree() call in
       * fill_in_insert_default_function_arguments() is an example. We mark the current node as not resolved so that it
       * is resolved again, this time filling in the default value. */
      node->info.name.spec_id = 0;
    }
  else
    {
      assert (false);
    }
}

/*
 * fill_in_insert_default_function_arguments () - Fills in the argument of the
 *                                                DEFAULT function when used
 *                                                for INSERT, MERGE INSERT
 *   parser(in):
 *   node(in):
 * Note: When parsing statements such as "INSERT INTO tbl VALUES (1, DEFAULT)"
 *       the column names corresponding to DEFAULT values are not yet known.
 *       When performing name resolution the names and default values can be
 *       filled in.
 */
static void
fill_in_insert_default_function_arguments (PARSER_CONTEXT * parser, PT_NODE * const node)
{
  PT_NODE *crt_attr = NULL;
  PT_NODE *crt_value = NULL;
  PT_NODE *crt_list = NULL;
  SM_CLASS *smclass = NULL;
  SM_ATTRIBUTE *attr = NULL;
  PT_NODE *cls_name = NULL;
  PT_NODE *values_list = NULL;
  PT_NODE *attrs_list = NULL;

  assert (node->node_type == PT_INSERT || node->node_type == PT_MERGE);

  /* if an attribute has a default expression as default value and that expression refers to the current date and time,
   * then we make sure that we mark this statement as one that needs the system datetime from the server */
  if (node->node_type == PT_INSERT)
    {
      cls_name = node->info.insert.spec->info.spec.entity_name;
      values_list = node->info.insert.value_clauses;
      attrs_list = node->info.insert.attr_list;
    }
  else
    {
      cls_name = node->info.merge.into->info.spec.entity_name;
      values_list = node->info.merge.insert.value_clauses;
      attrs_list = node->info.merge.insert.attr_list;
    }

  au_fetch_class_force (cls_name->info.name.db_object, &smclass, AU_FETCH_READ);
  if (smclass)
    {
      for (attr = smclass->attributes; attr != NULL; attr = (SM_ATTRIBUTE *) attr->header.next)
    {
      if (DB_IS_DATETIME_DEFAULT_EXPR (attr->default_value.default_expr.default_expr_type))
        {
          node->flag.si_datetime = true;
          db_make_null (&parser->sys_datetime);
          break;
        }
    }
    }

  for (crt_list = values_list; crt_list != NULL; crt_list = crt_list->next)
    {
      /*
       * If the statement such as "INSERT INTO tbl DEFAULT" is given,
       * we rewrite it to "INSERT INTO tbl VALUES (DEFAULT, DEFAULT, ...)"
       * to support "server-side insertion" simply.
       * In this situation, the server will get "default value" from
       * "original_value" of the current representation, but sometimes
       * it is not the latest default value. (See the comment for sm_attribute
       * structure on class_object.h for more information.)
       * However, the client always knows it, so it's better for the server to
       * get the value from the client.
       */
      if (crt_list->info.node_list.list_type == PT_IS_DEFAULT_VALUE)
    {
      PT_NODE *crt_value_list = NULL;

      assert ((node->node_type == PT_INSERT && node->info.node_list.list == NULL)
          || (node->node_type == PT_MERGE && crt_list->info.node_list.list == NULL));

      for (crt_attr = attrs_list; crt_attr != NULL; crt_attr = crt_attr->next)
        {
          crt_value = parser_new_node (parser, PT_EXPR);

          if (crt_value == NULL)
        {
          if (crt_value_list != NULL)
            {
              parser_free_tree (parser, crt_value_list);
            }
          PT_ERROR (parser, node, "allocation error");
          return;
        }

          crt_value->info.expr.op = PT_DEFAULTF;
          crt_value->info.expr.arg1 = parser_copy_tree (parser, crt_attr);

          if (crt_value->info.expr.arg1 == NULL)
        {
          parser_free_node (parser, crt_value);
          if (crt_value_list != NULL)
            {
              parser_free_tree (parser, crt_value_list);
            }
          PT_ERROR (parser, node, "allocation error");
          return;
        }

          pt_set_fill_default_in_path_expression (crt_value->info.expr.arg1);

          if (crt_value_list == NULL)
        {
          crt_value_list = crt_value;
        }
          else
        {
          crt_value_list = parser_append_node (crt_value, crt_value_list);
        }
        }
      crt_list->info.node_list.list = crt_value_list;
      crt_list->info.node_list.list_type = PT_IS_VALUE;
    }
      else
    {
      for (crt_attr = attrs_list, crt_value = crt_list->info.node_list.list; crt_attr != NULL && crt_value != NULL;
           crt_attr = crt_attr->next, crt_value = crt_value->next)
        {
          PT_NODE *crt_arg = NULL;

          if (crt_value->node_type != PT_EXPR)
        {
          continue;
        }
          if (crt_value->info.expr.op != PT_DEFAULTF)
        {
          continue;
        }
          if (crt_value->info.expr.arg1 != NULL)
        {
          continue;
        }
          crt_arg = parser_copy_tree (parser, crt_attr);
          if (crt_arg == NULL)
        {
          PT_ERROR (parser, node, "allocation error");
          return;
        }
          crt_value->info.expr.arg1 = crt_arg;
          pt_set_fill_default_in_path_expression (crt_arg);
        }
    }
    }
}

/*
 * pt_bind_names () -
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in/out):
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_bind_names (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_BIND_NAMES_ARG *bind_arg = (PT_BIND_NAMES_ARG *) arg;
  SCOPES scopestack;
  PT_NODE *node1 = NULL;
  PT_EXTRA_SPECS_FRAME spec_frame;
  PT_NODE *prev_attr = NULL, *attr = NULL, *next_attr = NULL, *as_attr = NULL;
  PT_NODE *resolved_attrs = NULL, *spec = NULL;
  PT_NODE *flat = NULL, *range_var = NULL;
  bool do_resolve = true;
  PT_NODE *seq = NULL;
  PT_NODE *cnf = NULL, *prev = NULL, *next = NULL, *last = NULL, *save = NULL;
  PT_NODE *lhs = NULL, *rhs = NULL, *lhs_spec = NULL, *rhs_spec = NULL;
  PT_NODE *p_spec = NULL;
  PT_NODE *result = NULL;
  short i, k, lhs_location, rhs_location, level;
  PT_JOIN_TYPE join_type;
  void *save_etc = NULL;
  PT_NODE *method_name_node = NULL;
  const char *method_name;

  if (!node || !parser || pt_has_error (parser))
    {
      return node;
    }

  *continue_walk = PT_CONTINUE_WALK;

  /* treat scopes as the next outermost scope */
  scopestack.next = bind_arg->scopes;
  scopestack.specs = NULL;
  scopestack.correlation_level = 0;
  scopestack.location = 0;

  /* prepend local scope to scopestack and then bind names */
  switch (node->node_type)
    {
    case PT_SELECT:

      scopestack.specs = node->info.query.q.select.from;
      spec_frame.next = bind_arg->spec_frames;
      spec_frame.extra_specs = NULL;

      /* break links to current scopes to bind_names in the WITH_CLAUSE */
      bind_arg->scopes = NULL;
      bind_arg->spec_frames = NULL;
      pt_bind_names_in_with_clause (parser, node, bind_arg);

      /* restore links to current scopes */
      bind_arg->scopes = &scopestack;
      bind_arg->spec_frames = &spec_frame;

      if (pt_has_error (parser))
    {
      /* name binding in WITH_CLAUSE have failed; this node will be registered to orphan list and freed later */
      node = NULL;
      goto select_end;
    }

      pt_bind_scope (parser, bind_arg);

      if (pt_has_error (parser))
    {
      /* this node will be registered to orphan list and freed later */
      node = NULL;
      goto select_end;
    }

      /* 0-step: check for hints that can affect name resolving. some hints are supposed to change the result type by
       * obtaining record information or page header information and so on. In these cases, names will be resolved to a
       * set of reserved names for each type of results. The query spec must be marked accordingly. NOTE: These hints
       * can be applied on single-spec queries. If this is a joined-spec query, just ignore the hints. */
      if (node->info.query.q.select.hint & PT_HINT_NO_PARALLEL_HEAP_SCAN)
    {
      for (PT_NODE * from = node->info.query.q.select.from; from != NULL; from = from->next)
        {
          from->info.spec.flag = (PT_SPEC_FLAG) (from->info.spec.flag | PT_SPEC_FLAG_NO_PARALLEL_HEAP_SCAN);
        }
    }

      if (node->info.query.q.select.hint & PT_HINT_PARALLEL)
    {
      for (PT_NODE * from = node->info.query.q.select.from; from != NULL; from = from->next)
        {
          from->info.spec.num_parallel_threads = node->info.query.q.select.num_parallel_threads;
          from->info.spec.flag = (PT_SPEC_FLAG) (from->info.spec.flag | PT_SPEC_FLAG_PARALLEL_THREAD);
        }
    }

      if (node->info.query.q.select.from != NULL && node->info.query.q.select.from->next == NULL)
    {
      if (node->info.query.q.select.hint & PT_HINT_SELECT_RECORD_INFO)
        {
          /* mark spec to scan for record info */
          node->info.query.q.select.from->info.spec.flag =
        (PT_SPEC_FLAG) (node->info.query.q.select.from->info.spec.flag | PT_SPEC_FLAG_RECORD_INFO_SCAN);
        }
      else if (node->info.query.q.select.hint & PT_HINT_SAMPLING_SCAN)
        {
          /* mark spec to scan for sampling scan */
          node->info.query.q.select.from->info.spec.flag =
        (PT_SPEC_FLAG) (node->info.query.q.select.from->info.spec.flag | PT_SPEC_FLAG_SAMPLING_SCAN);
        }
      else if (node->info.query.q.select.hint & PT_HINT_SELECT_PAGE_INFO)
        {
          /* mark spec to scan for heap page headers */
          node->info.query.q.select.from->info.spec.flag =
        (PT_SPEC_FLAG) (node->info.query.q.select.from->info.spec.flag | PT_SPEC_FLAG_PAGE_INFO_SCAN);
        }
      else if (node->info.query.q.select.hint & PT_HINT_SELECT_KEY_INFO)
        {
          PT_NODE *using_index = node->info.query.q.select.using_index;
          if (using_index == NULL || !PT_IS_NAME_NODE (using_index))
        {
          assert (0);
          PT_INTERNAL_ERROR (parser, "Invalid usage of SELECT_KEY_INFO hint");
          parser_free_tree (parser, node);
          node = NULL;
          goto select_end;
        }
          /* using_index is just a name, mark as index name */
          using_index->info.name.meta_class = PT_INDEX_NAME;
          using_index->etc = (void *) PT_IDX_HINT_FORCE;

          /* mark spec to scan for index key info */
          node->info.query.q.select.from->info.spec.flag =
        (PT_SPEC_FLAG) (node->info.query.q.select.from->info.spec.flag | PT_SPEC_FLAG_KEY_INFO_SCAN);
        }
      else if (node->info.query.q.select.hint & PT_HINT_SELECT_BTREE_NODE_INFO)
        {
          PT_NODE *using_index = node->info.query.q.select.using_index;
          if (using_index == NULL || !PT_IS_NAME_NODE (using_index))
        {
          assert (0);
          PT_INTERNAL_ERROR (parser, "Invalid usage of SELECT_KEY_INFO hint");
          parser_free_tree (parser, node);
          node = NULL;
          goto select_end;
        }
          /* using_index is just a name, mark as index name */
          using_index->info.name.meta_class = PT_INDEX_NAME;
          using_index->etc = (void *) PT_IDX_HINT_FORCE;

          /* mark spec to scan for index key info */
          node->info.query.q.select.from->info.spec.flag =
        (PT_SPEC_FLAG) (node->info.query.q.select.from->info.spec.flag | PT_SPEC_FLAG_BTREE_NODE_INFO_SCAN);
        }
    }

      /* resolve '*' for rewritten multicolumn subquery during parsing STEP 1: remove sequence from select_list STEP 2:
       * resolve '*', if exists STEP 3: restore sequence */

      /* STEP 1 */
      seq = NULL;
      if (node->info.query.q.select.list->node_type == PT_VALUE
      && node->info.query.q.select.list->type_enum == PT_TYPE_SEQUENCE
      && pt_length_of_select_list (node->info.query.q.select.list, EXCLUDE_HIDDEN_COLUMNS) == 1)
    {
      seq = node->info.query.q.select.list;
      node->info.query.q.select.list = seq->info.value.data_value.set;
      seq->info.value.data_value.set = NULL;    /* cut-off link */
    }

      /* STEP 2 */
      if (node->info.query.q.select.list)
    {           /* resolve "*" */
      if (node->info.query.q.select.list->node_type == PT_VALUE
          && node->info.query.q.select.list->type_enum == PT_TYPE_STAR)
        {
          PT_NODE *next = node->info.query.q.select.list->next;

          /* To consider 'select *, xxx ...', release "*" node only. */
          node->info.query.q.select.list->next = NULL;
          parser_free_node (parser, node->info.query.q.select.list);

          node->info.query.q.select.list = pt_resolve_star (parser, node->info.query.q.select.from, NULL);

          if (next != NULL)
        {
          parser_append_node (next, node->info.query.q.select.list);
        }

          if (!node->info.query.q.select.list)
        {
          unsigned int save_custom = parser->custom_print;
          PT_NODE *from = node->info.query.q.select.from;

          parser->custom_print = parser->custom_print | PT_SUPPRESS_RESOLVED;
          PT_ERRORmf (parser, from, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NO_ATTRIBUTES_IN_CLS,
                  pt_short_print (parser, from));
          parser->custom_print = save_custom;
          parser_free_tree (parser, node);
          node = NULL;
          goto select_end;
        }
        }
      else
        {           /* resolve "class_name.*" */
          prev_attr = NULL;
          attr = node->info.query.q.select.list;

          while (attr && do_resolve)
        {
          do_resolve = false;

          /* STEP 2-1) find "class_name.*" */
          while (attr)
            {
              if (attr->node_type == PT_NAME && attr->type_enum == PT_TYPE_STAR)
            {
              /* find "class_name.*" */
              do_resolve = true;
              break;
            }

              prev_attr = attr; /* save previous attr */
              attr = attr->next;
            }

          if (attr == NULL) /* consume attr list */
            {
              break;
            }

          /* STEP 2-2) do resolve */
          if (do_resolve == true)
            {
              /* STEP 2-2-1) assign spec_id into PT_NAME */
              for (spec = node->info.query.q.select.from; spec; spec = spec->next)
            {
              if (PT_SPEC_IS_ENTITY (spec))
                {
                  /* entity spec */
                  assert (!PT_SPEC_IS_DERIVED (spec) || !PT_SPEC_IS_CTE (spec));
                  flat = spec->info.spec.flat_entity_list;

                  if (pt_user_specified_name_compare (attr->info.name.original, flat->info.name.resolved) ==
                  0)
                {
                  /* find spec set attr's spec_id */
                  attr->info.name.spec_id = flat->info.name.spec_id;
                  break;
                }
                }
              else
                {
                  /* derived table or cte */
                  assert (PT_SPEC_IS_DERIVED (spec) || PT_SPEC_IS_CTE (spec));
                  range_var = spec->info.spec.range_var;
                  if (pt_user_specified_name_compare
                  (attr->info.name.original, range_var->info.name.original) == 0)
                {
                  break;
                }
                }
            }   /* for */

              if (spec == NULL)
            {   /* error */
              do_resolve = false;
              node->info.query.q.select.list = NULL;
              break;
            }
              else
            {
              /* STEP 2-2-2) recreate select_list */
              if (PT_SPEC_IS_ENTITY (spec))
                {
                  resolved_attrs =
                parser_append_node (attr->next,
                            pt_resolve_star (parser, node->info.query.q.select.from, attr));
                }
              else
                {
                  for (as_attr = spec->info.spec.as_attr_list; as_attr; as_attr = as_attr->next)
                {
                  as_attr->info.name.resolved = range_var->info.name.original;
                }
                  resolved_attrs =
                parser_append_node (attr->next,
                            parser_copy_tree_list (parser, spec->info.spec.as_attr_list));
                }

              if (prev_attr == NULL)
                {
                  node->info.query.q.select.list = resolved_attrs;
                }
              else
                {
                  prev_attr->next = NULL;
                  node->info.query.q.select.list =
                parser_append_node (resolved_attrs, node->info.query.q.select.list);
                }

              if (resolved_attrs == NULL || node->info.query.q.select.list == NULL)
                {
                  node->info.query.q.select.list = NULL;
                  break;
                }
            }
            }       /* if (do_resolve) */

          next_attr = attr->next;
          attr->next = NULL;
          parser_free_tree (parser, attr);
          attr = next_attr;

          /* reposition prev_attr */
          for (prev_attr = resolved_attrs; prev_attr->next != attr; prev_attr = prev_attr->next)
            {
              ;
            }
        }
        }

      if (!node->info.query.q.select.list)
        {
          unsigned int save_custom = parser->custom_print;

          parser->custom_print = (parser->custom_print | PT_SUPPRESS_RESOLVED);
          PT_ERRORmf (parser, attr, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_DEFINED,
              pt_short_print (parser, attr));
          parser->custom_print = save_custom;
          parser_free_tree (parser, node);
          node = NULL;
          goto select_end;
        }
    }

      /* STEP 3 */
      if (seq)
    {
      seq->info.value.data_value.set = node->info.query.q.select.list;
      node->info.query.q.select.list = seq;
    }

      (void) pt_resolve_hint (parser, node);

      parser_walk_leaves (parser, node, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);

      /* capture minimum correlation */
      if (!node->info.query.correlation_level)
    {
      node->info.query.correlation_level = scopestack.correlation_level;
    }
      else if (scopestack.correlation_level && (scopestack.correlation_level < node->info.query.correlation_level))
    {
      node->info.query.correlation_level = scopestack.correlation_level;
    }

      /* capture type enum and data_type from first column in select list */
      if (node && node->info.query.q.select.list)
    {
      node->type_enum = node->info.query.q.select.list->type_enum;
      if (node->info.query.q.select.list->data_type)
        {
          node->data_type = parser_copy_tree_list (parser, node->info.query.q.select.list->data_type);
        }
    }

      /* pop the extra spec frame and add any extra specs to the from list */
      node->info.query.q.select.from = parser_append_node (spec_frame.extra_specs, node->info.query.q.select.from);

      /*
       * Oracle style outer join support: convert to ANSI standard style
       * only permit the following predicate
       * 'single_column(+) op expression_'
       * 'expression_      op single_column(+)'
       * move outer join predicates from WHERE clause
       * to ON condition in FROM clause and set proper join type
       */

      if (!PT_SELECT_INFO_IS_FLAGED (node, PT_SELECT_INFO_ORACLE_OUTER))
    {
      /* not found */
      goto select_end;
    }

      if (PT_SELECT_INFO_IS_FLAGED (node, PT_SELECT_INFO_ANSI_JOIN))
    {
      /* can not specify the (+) operator in a query that also conatins FROM clause join syntax */
      PT_ERROR (parser, node, "can not specify '(+)' with ANSI join syntax");
      parser_free_tree (parser, node);
      node = NULL;
      goto select_end;
    }

#if 0
      /* By default, support oracle_style_outerjoin */
      if (!prm_get_bool_value (PRM_ID_ORACLE_STYLE_OUTERJOIN))
    {
      PT_ERROR (parser, node, "Oracle outer join conversion not supported");
      parser_free_tree (parser, node);
      node = NULL;
      goto select_end;
    }
#endif /* 0 */

      /* convert to CNF and tag taggable terms */
      node->info.query.q.select.where = pt_cnf (parser, node->info.query.q.select.where);

      /* list search twice one-pass: set outer join type, edge two-pass: set outer join sarg outer join sarg matrix:
       * X.i=Y.i and Y.i=Z.i and Y.j=1 case 1: X right Y right Z -> put Y(+)-sarg to Z case 2: X right Y left Z ->
       * meaningless Y(+)-sarg case 3: X left Y right Z -> syntax error at one-pass case 4: X left Y left Z -> put
       * Y(+)-sarg to Y */

      for (k = 1; k <= 2; k++)
    {
      prev = NULL;      /* init */
      for (cnf = node->info.query.q.select.where; cnf; cnf = next)
        {
          next = cnf->next; /* save link */

          if (cnf->node_type != PT_EXPR)
        {
          prev = cnf;
          continue;
        }

          if (PT_EXPR_INFO_IS_FLAGED (cnf, PT_EXPR_INFO_LEFT_OUTER))
        {
          join_type = PT_JOIN_LEFT_OUTER;
        }
          else if (PT_EXPR_INFO_IS_FLAGED (cnf, PT_EXPR_INFO_RIGHT_OUTER))
        {
          join_type = PT_JOIN_RIGHT_OUTER;
        }
          else
        {
          prev = cnf;
          continue;
        }

          /* find left-hand side spec */
          lhs = cnf->info.expr.arg1;
          lhs_spec = NULL;
          lhs_location = 0; /* init */
          if (lhs)
        {
          if (pt_is_attr (lhs))
            {
              if (lhs->node_type == PT_DOT_)
            {
              /* check for path expression */
              PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUTERJOIN_PATH_EXPR,
                      pt_short_print (parser, node));
              parser_free_tree (parser, node);
              node = NULL;
              goto select_end;
            }

              /* find the class of the lhs from spec list */
              for (i = 0, spec = node->info.query.q.select.from; spec; i++, spec = spec->next)
            {
              /* found spec */
              if (spec->info.spec.id == lhs->info.name.spec_id)
                {
                  lhs_spec = spec;
                  lhs_location = i;
                  break;
                }
            }
            }
          else if (!pt_is_const (lhs))  /* is_const : no nothing */
            {
              /* find the class of the lhs from spec list */
              last = save = NULL;   /* init */
              do
            {
              for (i = -1, spec = node->info.query.q.select.from; spec && spec != last;
                   i++, spec = spec->next)
                {
                  save = spec;
                }
              last = save;  /* set last spec */

              /* traverse tree to find spec */
              bind_arg->sc_info->Oracle_outerjoin_spec = last;
              bind_arg->sc_info->Oracle_outerjoin_attr_num = 0;
              bind_arg->sc_info->Oracle_outerjoin_subq_num = 0;
              bind_arg->sc_info->Oracle_outerjoin_path_num = 0;
              parser_walk_tree (parser, lhs, pt_check_Oracle_outerjoin, bind_arg, pt_continue_walk, NULL);
              if (bind_arg->sc_info->Oracle_outerjoin_attr_num > 0)
                {
                  lhs_spec = last;
                  lhs_location = i;
                }
              /* check for subquery */
              if (bind_arg->sc_info->Oracle_outerjoin_subq_num > 0)
                {
                  PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC,
                     MSGCAT_SEMANTIC_OUTERJOIN_JOIN_COND_SUBQ);
                  parser_free_tree (parser, node);
                  node = NULL;
                  goto select_end;
                }
              /* check for path expression */
              if (bind_arg->sc_info->Oracle_outerjoin_path_num > 0)
                {
                  PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUTERJOIN_PATH_EXPR,
                      pt_short_print (parser, node));
                  parser_free_tree (parser, node);
                  node = NULL;
                  goto select_end;
                }
            }
              while (lhs_spec == NULL && i > 0);
            }
        }       /* if (lhs) */

          /* find right-hand side spec */
          rhs = cnf->info.expr.arg2;
          rhs_spec = NULL;
          rhs_location = 0; /* init */
          if (rhs)
        {
          if (pt_is_attr (rhs))
            {
              if (rhs->node_type == PT_DOT_)
            {
              /* check for path expression */
              PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUTERJOIN_PATH_EXPR,
                      pt_short_print (parser, node));
              parser_free_tree (parser, node);
              node = NULL;
              goto select_end;
            }

              /* find the class of the rhs from spec list */
              for (i = 0, spec = node->info.query.q.select.from; spec; i++, spec = spec->next)
            {
              /* found spec */
              if (spec->info.spec.id == rhs->info.name.spec_id)
                {
                  rhs_spec = spec;
                  rhs_location = i;
                  break;
                }
            }
            }
          else if (!pt_is_const (rhs))  /* is_const : do nothing */
            {
              /* find the class of the rhs from spec list */
              last = save = NULL;   /* init */
              do
            {
              for (i = -1, spec = node->info.query.q.select.from; spec && spec != last;
                   i++, spec = spec->next)
                {
                  save = spec;
                }
              last = save;  /* set last spec */

              /* traverse tree to find spec */
              bind_arg->sc_info->Oracle_outerjoin_spec = last;
              bind_arg->sc_info->Oracle_outerjoin_attr_num = 0;
              bind_arg->sc_info->Oracle_outerjoin_subq_num = 0;
              bind_arg->sc_info->Oracle_outerjoin_path_num = 0;
              parser_walk_tree (parser, rhs, pt_check_Oracle_outerjoin, bind_arg, pt_continue_walk, NULL);
              if (bind_arg->sc_info->Oracle_outerjoin_attr_num > 0)
                {
                  rhs_spec = last;
                  rhs_location = i;
                }
              /* check for subquery */
              if (bind_arg->sc_info->Oracle_outerjoin_subq_num > 0)
                {
                  PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC,
                     MSGCAT_SEMANTIC_OUTERJOIN_JOIN_COND_SUBQ);
                  parser_free_tree (parser, node);
                  node = NULL;
                  goto select_end;
                }
              /* check for path expression */
              if (bind_arg->sc_info->Oracle_outerjoin_path_num > 0)
                {
                  PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUTERJOIN_PATH_EXPR,
                      pt_short_print (parser, node));
                  parser_free_tree (parser, node);
                  node = NULL;
                  goto select_end;
                }
            }
              while (rhs_spec == NULL && i > 0);
            }
        }       /* if (rhs) */

          if (k == 1)
        {
          /* first search: only consider edges and skip sargs */
          if (lhs_spec && rhs_spec && lhs_spec->info.spec.id != rhs_spec->info.spec.id)
            {
              /* found edge: set join type and spec */
              if (lhs_location < rhs_location)
            {
              p_spec = lhs_spec;
              spec = rhs_spec;
            }
              else
            {
              /* converse join type of edge for example SELECT...  FROM x, y WHERE y.i = x.i(+) */
              join_type = (join_type == PT_JOIN_LEFT_OUTER) ? PT_JOIN_RIGHT_OUTER : PT_JOIN_LEFT_OUTER;
              p_spec = rhs_spec;
              spec = lhs_spec;
            }
            }
          else
            {
              prev = cnf;
              continue;
            }
        }
          else
        {
          p_spec = NULL;    /* useless */
          spec = (lhs_location < rhs_location) ? rhs_spec : lhs_spec;
        }

          /* error check */
          if (spec == NULL)
        {
          /* give up */
          PT_ERRORf (parser, node, "check outer join syntax at '%s'", pt_short_print (parser, cnf));
          parser_free_tree (parser, node);
          node = NULL;
          goto select_end;
        }

          if (k == 1)
        {
          /* first search: check outer join link set join type and edge */

          PT_NODE *tmp, *p_end, *s_end, *start, *end, *s_start;

          if (spec->info.spec.join_type == PT_JOIN_NONE)
            {
              /* case A, C */
              p_end = p_spec;   /* init */
              for (tmp = p_end; tmp->next != spec; tmp = tmp->next)
            {
              /* found end of p_spec join_list */
              if (tmp->next->info.spec.join_type == PT_JOIN_NONE)
                {
                  p_end = tmp;
                  break;
                }
              else
                {
                  p_end = tmp->next;
                }
            }
              for (tmp = p_end; tmp->next != spec; tmp = tmp->next)
            {
              ;
            }
              /* found before node of spec join list: cut-off link */
              tmp->next = NULL;

              for (tmp = spec; tmp->next; tmp = tmp->next)
            {
              ;
            }
              /* found end of spec list */
              s_end = tmp;

              s_end->next = p_end->next;
              p_end->next = spec;
            }
          else
            {
              for (tmp = p_spec->next; tmp != spec; tmp = tmp->next)
            {
              /* join_list disconnected */
              if (tmp->info.spec.join_type == PT_JOIN_NONE)
                {
                  break;
                }
            }
              if (tmp != spec)
            {   /* p_spec to spec join_list not exists */
              if (p_spec->info.spec.join_type == PT_JOIN_NONE)
                {
                  /* case B */
                  s_start = NULL;   /* init */
                  start = tmp;  /* init */
                  end = spec;   /* init */
                  do
                {
                  for (tmp = start; tmp->next != end; tmp = tmp->next)
                    {
                      ;
                    }
                  if (tmp->info.spec.join_type == PT_JOIN_NONE)
                    {
                      /* found spec join_link start */
                      s_start = tmp;
                      for (tmp = node->info.query.q.select.from; tmp->next != s_start; tmp = tmp->next)
                    {
                      ;
                    }
                      /* found before node of spec join_list: cut-off link */
                      tmp->next = NULL;
                    }
                  else
                    {
                      end = tmp;
                    }
                }
                  while (s_start == NULL);  /* end do */

                  for (tmp = spec; tmp->next; tmp = tmp->next)
                {
                  ;
                }
                  /* found end of spec list */
                  s_end = tmp;

                  s_end->next = node->info.query.q.select.from;
                  node->info.query.q.select.from = s_start;

                  /* swap p_spec, spec */
                  save = p_spec;
                  p_spec = spec;
                  spec = save;

                  /* converse join type */
                  join_type = (join_type == PT_JOIN_LEFT_OUTER) ? PT_JOIN_RIGHT_OUTER : PT_JOIN_LEFT_OUTER;

                  /* move unnecessary spec list located between the two swapped specs to the end of the entire spec list */
                  p_end = NULL;
                  s_start = NULL;
                  s_end = NULL;

                  for (tmp = p_spec; tmp && tmp->next; tmp = tmp->next)
                {
                  if (!s_start)
                    {
                      /* cannot find the spec to move */
                      if (tmp->next == spec)
                    {
                      break;
                    }

                      /* find the head node of the spec list to be moved
                       * find the previous node to detach the head node */
                      if (tmp->next->info.spec.join_type == PT_JOIN_NONE)
                    {
                      p_end = tmp;
                      s_start = tmp->next;
                    }
                    }

                  else
                    {
                      /* find the tail node of the spec list to be moved */
                      if (tmp->next == spec)
                    {
                      s_end = tmp;
                    }
                    }
                }

                  if (s_start && s_end && p_end)
                {
                  s_end->next = NULL;
                  p_end->next = spec;
                  tmp->next = s_start;
                }
                }
              else
                {
                  /* case D: give up */
                  PT_ERROR (parser, node, "a class may be outer joined to" " at most one other class");
                  parser_free_tree (parser, node);
                  node = NULL;
                  goto select_end;
                }
            }
            }
          /* outer join_link handling end */

          /* set join type */
          if ((spec->info.spec.join_type == PT_JOIN_LEFT_OUTER
               || spec->info.spec.join_type == PT_JOIN_RIGHT_OUTER) && spec->info.spec.join_type != join_type)
            {
              /* give up */
              PT_ERROR (parser, node, "two classes cannot be " "outer-joined to each other");
              parser_free_tree (parser, node);
              node = NULL;
              goto select_end;
            }
          spec->info.spec.join_type = join_type;
        }
          else
        {       /* k == 2 */
          /* second search: set sarg */
          if (spec->info.spec.join_type == PT_JOIN_RIGHT_OUTER && spec->next)
            {
              if (spec->next->info.spec.join_type == PT_JOIN_RIGHT_OUTER)
            {
              /* check for case 1: */
              spec = spec->next;
            }
              else if (spec->next->info.spec.join_type == PT_JOIN_LEFT_OUTER)
            {
              /* check for case 2: meaningless outer join sargs */
              prev = cnf;
              continue;
            }
            }

          /* check for case 4: do nothing and go ahead */
        }

          /* cut-off cnf from WHERE clause */
          if (prev == NULL)
        {       /* the first cnf */
          node->info.query.q.select.where = next;
        }
          else
        {
          prev->next = next;
        }
          cnf->next = NULL; /* cut-off link */

          PT_EXPR_INFO_CLEAR_FLAG (cnf, PT_EXPR_INFO_LEFT_OUTER | PT_EXPR_INFO_RIGHT_OUTER);

          /* put cnf to the ON cond */
          spec->info.spec.on_cond = parser_append_node (cnf, spec->info.spec.on_cond);
        }
    }

      for (spec = node->info.query.q.select.from; spec; spec = spec->next)
    {
      if (spec->info.spec.on_cond)
        {
          /* check for case 3: */
          if (spec->info.spec.join_type == PT_JOIN_LEFT_OUTER && spec->next
          && spec->next->info.spec.join_type == PT_JOIN_RIGHT_OUTER)
        {
          PT_ERROR (parser, node, "a class may be outer joined to " "at most one other class");
          parser_free_tree (parser, node);
          node = NULL;
          goto select_end;
        }
          else if (spec->info.spec.join_type == PT_JOIN_RIGHT_OUTER)
        {
          for (save = node->info.query.q.select.from; save; save = save->next)
            {
              if (save->next == spec)
            {
              if (save->info.spec.join_type == PT_JOIN_LEFT_OUTER)
                {
                  PT_ERROR (parser, node, "a class may be outer joined to " "at most one other class");
                  parser_free_tree (parser, node);
                  node = NULL;
                  goto select_end;
                }
            }
            }       /* for */
        }


          /* clear spec_id to re-check outer join semantic */
          bind_arg->sc_info->Oracle_outerjoin_spec = spec;
          parser_walk_tree (parser, spec->info.spec.on_cond, pt_clear_Oracle_outerjoin_spec_id, bind_arg,
                pt_continue_walk, NULL);

#if !defined (NDEBUG)
          assert (!PT_EXPR_INFO_IS_FLAGED (spec->info.spec.on_cond,
                           PT_EXPR_INFO_LEFT_OUTER | PT_EXPR_INFO_RIGHT_OUTER));
#endif
        }
    }

      /* check outer join semantic of ON cond */
      bind_arg->scopes->specs = node->info.query.q.select.from; /* init */
      bind_arg->scopes->location = 0;   /* init */
      parser_walk_tree (parser, node->info.query.q.select.from, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);

      /* meaningless outer join check */
      for (spec = node->info.query.q.select.from; spec; spec = spec->next)
    {
      if (spec->info.spec.join_type == PT_JOIN_NONE && spec->info.spec.on_cond)
        {
          /* meaningless outer join predicate for example SELECT...  FROM x, y WHERE x.i(+) = 1 recover to WHERE
           * clause */
          node->info.query.q.select.where =
        parser_append_node (spec->info.spec.on_cond, node->info.query.q.select.where);
          spec->info.spec.on_cond = NULL;
        }
    }           /* for */

#if !defined (NDEBUG)
      for (cnf = node->info.query.q.select.where; cnf; cnf = cnf->next)
    {
      assert (!PT_EXPR_INFO_IS_FLAGED (cnf, PT_EXPR_INFO_LEFT_OUTER | PT_EXPR_INFO_RIGHT_OUTER));
    }
#endif

      if (PT_SELECT_INFO_IS_FLAGED (node, PT_SELECT_INFO_ORACLE_OUTER))
    {
      PT_SELECT_INFO_CLEAR_FLAG (node, PT_SELECT_INFO_ORACLE_OUTER);
      PT_SELECT_INFO_SET_FLAG (node, PT_SELECT_INFO_ANSI_JOIN);
    }

    select_end:
      bind_arg->spec_frames = bind_arg->spec_frames->next;

      /* remove this level's scope */
      bind_arg->scopes = bind_arg->scopes->next;

      /* don't revisit leaves */
      *continue_walk = PT_LIST_WALK;
      break;

    case PT_UNION:
    case PT_INTERSECTION:
    case PT_DIFFERENCE:
      {
    int arg1_corr, arg2_corr, corr;
    PT_NODE *arg1, *arg2;
    PT_NODE *select_node = NULL, *order_by_link = NULL;
    int index_of_order_by_link = -1;

    /* treat this just like a select with no from, so that we can properly get correlation level of sub-queries. */
    bind_arg->scopes = &scopestack;

    pt_bind_names_in_with_clause (parser, node, bind_arg);

    /* change order by link in UNION/INTERSECTION/DIFFERENCE query into the tail of first select query's order by
     * list for bind names (It will be restored after bind names.) */

    if (node->info.query.order_by)
      {
        index_of_order_by_link = 0;

        select_node = node;
        while (select_node)
          {
        switch (select_node->node_type)
          {
          case PT_SELECT:
            goto l_select_node;
            break;
          case PT_UNION:
          case PT_INTERSECTION:
          case PT_DIFFERENCE:
            select_node = select_node->info.query.q.union_.arg1;
            break;
          default:
            select_node = NULL;
            break;
          }
          }

      l_select_node:
        if (select_node)
          {
        if (!select_node->info.query.order_by)
          {
            select_node->info.query.order_by = node->info.query.order_by;
          }
        else
          {
            index_of_order_by_link++;
            order_by_link = select_node->info.query.order_by;
            while (order_by_link->next)
              {
            order_by_link = order_by_link->next;
            index_of_order_by_link++;
              }

            order_by_link->next = node->info.query.order_by;
          }

        node->info.query.order_by = NULL;
          }
      }

    parser_walk_leaves (parser, node, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);

    arg1 = node->info.query.q.union_.arg1;
    arg2 = node->info.query.q.union_.arg2;

    if (arg1 && arg2)
      {
        arg1_corr = arg1->info.query.correlation_level;
        arg2_corr = arg2->info.query.correlation_level;
        if (arg1_corr)
          {
        corr = arg1_corr;
        if (arg2_corr)
          {
            if (arg2_corr < corr)
              corr = arg2_corr;
          }
          }
        else
          {
        corr = arg2_corr;
          }
        /* must reduce the correlation level 1, for this level of scoping */
        if (corr)
          {
        corr--;
          }
        node->info.query.correlation_level = corr;

        /* capture type enum and data_type from arg1 */
        node->type_enum = arg1->type_enum;
        if (arg1->data_type)
          node->data_type = parser_copy_tree_list (parser, arg1->data_type);
      }

    /* Restore order by link */
    if (index_of_order_by_link >= 0)
      {
        if (order_by_link)
          {
        node->info.query.order_by = order_by_link->next;
        order_by_link->next = NULL;
          }
        else
          {
        node->info.query.order_by = select_node->info.query.order_by;
        select_node->info.query.order_by = NULL;
          }
      }
      }

      /* remove this level's scope */
      bind_arg->scopes = bind_arg->scopes->next;

      /* don't revisit leaves */
      *continue_walk = PT_LIST_WALK;
      break;

    case PT_UPDATE:
      scopestack.specs = node->info.update.spec;
      spec_frame.next = bind_arg->spec_frames;
      spec_frame.extra_specs = NULL;

      /* break links to current scopes to bind_names in the WITH_CLAUSE */
      bind_arg->scopes = NULL;
      bind_arg->spec_frames = NULL;
      pt_bind_names_in_with_clause (parser, node, bind_arg);

      bind_arg->spec_frames = spec_frame.next;

      /* restore links to current scopes */
      bind_arg->scopes = &scopestack;
      spec_frame.next = bind_arg->spec_frames;
      spec_frame.extra_specs = NULL;
      bind_arg->spec_frames = &spec_frame;
      pt_bind_scope (parser, bind_arg);

      (void) pt_resolve_hint (parser, node);

      parser_walk_leaves (parser, node, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);

      /* pop the extra spec frame and add any extra specs to the from list */
      bind_arg->spec_frames = bind_arg->spec_frames->next;
      node->info.update.class_specs = parser_append_node (spec_frame.extra_specs, node->info.update.class_specs);

      /* remove this level's scope */
      bind_arg->scopes = bind_arg->scopes->next;

      /* don't revisit leaves */
      *continue_walk = PT_LIST_WALK;
      break;

    case PT_DELETE:
      scopestack.specs = node->info.delete_.spec;
      spec_frame.next = bind_arg->spec_frames;
      spec_frame.extra_specs = NULL;

      /* break links to current scopes to bind_names in the WITH_CLAUSE */
      bind_arg->scopes = NULL;
      bind_arg->spec_frames = NULL;
      pt_bind_names_in_with_clause (parser, node, bind_arg);

      bind_arg->spec_frames = spec_frame.next;

      bind_arg->scopes = &scopestack;
      spec_frame.next = bind_arg->spec_frames;
      spec_frame.extra_specs = NULL;
      bind_arg->spec_frames = &spec_frame;
      pt_bind_scope (parser, bind_arg);

      (void) pt_resolve_hint (parser, node);

      parser_walk_leaves (parser, node, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);

      /* pop the extra spec frame and add any extra specs to the from list */
      bind_arg->spec_frames = bind_arg->spec_frames->next;
      node->info.delete_.class_specs = parser_append_node (spec_frame.extra_specs, node->info.delete_.class_specs);

      /* remove this level's scope */
      bind_arg->scopes = bind_arg->scopes->next;

      /* don't revisit leaves */
      *continue_walk = PT_LIST_WALK;
      break;

    case PT_INSERT:
      scopestack.specs = node->info.insert.spec;
      bind_arg->scopes = &scopestack;
      spec_frame.next = bind_arg->spec_frames;
      spec_frame.extra_specs = NULL;
      bind_arg->spec_frames = &spec_frame;
      pt_bind_scope (parser, bind_arg);

      result = pt_resolve_vclass_args (parser, node);
      if (!result)
    {
      /* error is handled */
      goto insert_end;
    }
      node = result;

      if (!node->info.insert.attr_list)
    {
      node->info.insert.attr_list = pt_resolve_star (parser, node->info.insert.spec, NULL);
    }

      fill_in_insert_default_function_arguments (parser, node);

      /* Do not handle ON DUPLICATE KEY UPDATE yet, we need to resolve the other nodes first. */
      save = node->info.insert.odku_assignments;
      node->info.insert.odku_assignments = NULL;

      parser_walk_leaves (parser, node, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);

      /* Check for double assignments */
      pt_no_double_insert_assignments (parser, node);
      if (pt_has_error (parser))
    {
      goto insert_end;
    }

      /* flag any "correlated" names as undefined. only names in subqueries and sub-inserts should be undefined. use
       * spec->etc to store the correlation level in value_clauses. */
      save_etc = node->info.insert.spec->etc;
      level = 0;
      node->info.insert.spec->etc = &level;
      parser_walk_tree (parser, node->info.insert.value_clauses, pt_undef_names_pre, node->info.insert.spec,
            pt_undef_names_post, node->info.insert.spec);
      node->info.insert.spec->etc = save_etc;

      if (save != NULL)
    {
      SCOPES extended_scope;
      PT_NODE *value_list = node->info.insert.value_clauses->info.node_list.list;
      extended_scope.next = NULL;

      /* restore ON DUPLICATE KEY UPDATE node */
      node->info.insert.odku_assignments = save;

      /* pt_undef_names_pre may have generated an error */
      if (pt_has_error (parser))
        {
          goto insert_end;
        }

      if (PT_IS_SELECT (value_list))
        {
          /* Some assignments may reference attributes from the select query that need to be resolved too. Add the
           * specs from the select statement as a scope in the stack. */
          extended_scope.next = bind_arg->scopes->next;
          extended_scope.specs = value_list->info.query.q.select.from;
          scopestack.correlation_level = 0;
          scopestack.location = 0;
          bind_arg->scopes->next = &extended_scope;
        }

      parser_walk_tree (parser, node->info.insert.odku_assignments, pt_bind_names, bind_arg, pt_bind_names_post,
                bind_arg);

      if (PT_IS_SELECT (value_list))
        {
          /* restore original scopes */
          bind_arg->scopes->next = extended_scope.next;
        }
    }

      if (!pt_has_error (parser))
    {
      /* make sure attributes were not bound to parameters */
      for (attr = node->info.insert.attr_list; attr; attr = attr->next)
        {
          if (attr->info.name.meta_class == PT_PARAMETER)
        {
          /* this is not an attribute of insert spec */
          PT_ERRORmf2 (parser, attr, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NOT_ATTRIBUTE_OF,
                   attr->info.name.original,
                   node->info.insert.spec->info.spec.entity_name->info.name.original);
        }
        }
    }

    insert_end:
      /* pop the extra spec frame and add any extra specs to the from list */
      bind_arg->spec_frames = bind_arg->spec_frames->next;
      node->info.insert.class_specs = parser_append_node (spec_frame.extra_specs, node->info.insert.class_specs);

      /* remove this level's scope */
      bind_arg->scopes = bind_arg->scopes->next;

      /* don't revisit leaves */
      *continue_walk = PT_LIST_WALK;
      break;

    case PT_MERGE:
      if (node->info.merge.into->info.spec.remote_server_name)
    {
      ;
    }
      else
    {
      if (node->info.merge.insert.value_clauses)
        {
          /* resolve missing attr_list as star */
          if (!node->info.merge.insert.attr_list)
        {
          node->info.merge.insert.attr_list = pt_resolve_star (parser, node->info.merge.into, NULL);
        }
          /* resolve DEFAULT clauses */
          if (node->info.merge.into->info.spec.entity_name)
        {
          fill_in_insert_default_function_arguments (parser, node);
        }
          /* resolve insert attributes, values */
          pt_bind_names_merge_insert (parser, node, bind_arg, &scopestack, &spec_frame);
          if (pt_has_error (parser))
        {
          node = NULL;
          *continue_walk = PT_STOP_WALK;
          break;
        }
        }

      if (node->info.merge.update.assignment)
        {
          /* resolved update assignment list */
          pt_bind_names_merge_update (parser, node, bind_arg, &scopestack, &spec_frame);
          if (pt_has_error (parser))
        {
          node = NULL;
          *continue_walk = PT_STOP_WALK;
          break;
        }
        }
    }

      assert (node->info.merge.into->next == NULL);
      node->info.merge.into->next = node->info.merge.using_clause;

      scopestack.specs = node->info.merge.into;
      bind_arg->scopes = &scopestack;
      spec_frame.next = bind_arg->spec_frames;
      spec_frame.extra_specs = NULL;
      bind_arg->spec_frames = &spec_frame;
      pt_bind_scope (parser, bind_arg);
      if (pt_has_error (parser))
    {
      node = NULL;
      *continue_walk = PT_STOP_WALK;
      break;
    }

      if (node->info.merge.into->info.spec.remote_server_name == NULL)
    {
      parser_walk_leaves (parser, node, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);
    }

      if (pt_has_error (parser))
    {
      node = NULL;
      *continue_walk = PT_STOP_WALK;
      break;
    }

      /* flag any "correlated" names as undefined. make sure etc is NULL. */
      save_etc = node->info.merge.into->etc;
      node->info.merge.into->etc = NULL;
      parser_walk_tree (parser, node->info.merge.insert.value_clauses, pt_undef_names_pre, node->info.merge.into, NULL,
            NULL);
      node->info.merge.into->etc = save_etc;
      if (pt_has_error (parser))
    {
      node = NULL;
      *continue_walk = PT_STOP_WALK;
      break;
    }

      bind_arg->spec_frames = bind_arg->spec_frames->next;
      bind_arg->scopes = bind_arg->scopes->next;

      /* don't revisit leaves */
      *continue_walk = PT_LIST_WALK;

      node->info.merge.into->next = NULL;
      break;

    case PT_CREATE_INDEX:
    case PT_ALTER_INDEX:
    case PT_DROP_INDEX:
      scopestack.specs = node->info.index.indexed_class;
      bind_arg->scopes = &scopestack;
      spec_frame.next = bind_arg->spec_frames;
      spec_frame.extra_specs = NULL;
      bind_arg->spec_frames = &spec_frame;
      pt_bind_scope (parser, bind_arg);

      parser_walk_leaves (parser, node, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);

      bind_arg->spec_frames = bind_arg->spec_frames->next;
      bind_arg->scopes = bind_arg->scopes->next;

      *continue_walk = PT_LIST_WALK;
      break;

    case PT_METHOD_CALL:
      /*
       * We accept two different method call syntax:
       *      1) method_name(...) on target
       *      2) method_name(target, ...)
       * We need to normalize the second to the first so that we can do
       * resolution without special cases.  We do this by moving the
       * first parameter to the on_call_target.  If there is no parameter,
       * it will be caught in pt_semantic_check_local()
       */
      method_name_node = node->info.method_call.method_name;
      // parser_print_tree is for built-in package names such as DBMS_OUTPUT
      if (PT_NAME_RESOLVED (method_name_node))
    {
      int custom_print_saved = parser->custom_print;
      parser->custom_print |= PT_SUPPRESS_QUOTES;
      parser->custom_print &= ~PT_PRINT_QUOTES;
      method_name = parser_print_tree (parser, method_name_node);
      parser->custom_print = custom_print_saved;
    }
      else
    {
      method_name = PT_NAME_ORIGINAL (method_name_node);
    }
      if (!node->info.method_call.on_call_target && jsp_is_exist_stored_procedure (method_name))
    {
      method_name_node->info.name.spec_id = (UINTPTR) method_name_node;
      node->info.method_call.method_type = (PT_MISC_TYPE) jsp_get_sp_type (method_name);
      method_name_node->info.name.meta_class = PT_METHOD;
      parser_walk_leaves (parser, node, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);
      /* don't revisit leaves */
      *continue_walk = PT_LIST_WALK;
    }
      else
    {
      if (!node->info.method_call.on_call_target && node->info.method_call.arg_list)
        {
          node->info.method_call.on_call_target = node->info.method_call.arg_list;
          node->info.method_call.arg_list = node->info.method_call.arg_list->next;
          node->info.method_call.on_call_target->next = NULL;

          /*
           * When using a session variable in the first arg_list,
           * It is unknown whether the session variable contains a class, object, or constant value.
           * So, if it's not a Stored procedure and there is an on_call_target, then it's considered a method and [user_schema] is removed.
           *
           * ex) create class x (xint int, xstr string, class cint int) method add_int(int, int) int function add_int file '$METHOD_FILE';
           *     insert into x values (4, 'string 4');
           *     select x into p1 from x where xint = 4;
           *     call add_int(p1, 1, 2);
           */
          node->info.method_call.method_name->info.name.original =
        sm_remove_qualifier_name (node->info.method_call.method_name->info.name.original);
        }

      /* make method name look resolved */
      node->info.method_call.method_name->info.name.spec_id = (UINTPTR) node->info.method_call.method_name;
      node->info.method_call.method_name->info.name.meta_class = PT_METHOD;

      /*
       * bind the names in the method arguments and target, their
       * scope will be the same as the method node's scope
       */
      parser_walk_leaves (parser, node, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);
      /* don't revisit leaves */
      *continue_walk = PT_LIST_WALK;

      /* find the type of the method here */
      if (!pt_resolve_method_type (parser, node) && (node->info.method_call.call_or_expr != PT_IS_CALL_STMT))
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_METH_DOESNT_EXIST,
              node->info.method_call.method_name->info.name.original);
          break;
        }

      /* if it is a call statement, we don't want to resolve method_name. also, if scopes is NULL we assume this
       * came from an evaluate call and we treat it like a call statement */
      if ((node->info.method_call.call_or_expr == PT_IS_CALL_STMT) || (bind_arg->scopes == NULL))
        {
          break;
        }

      /* resolve method name to entity where expansion will take place */
      if (node->info.method_call.on_call_target->node_type == PT_NAME
          && (node->info.method_call.on_call_target->info.name.meta_class != PT_PARAMETER))
        {
          PT_NODE *entity = pt_find_entity_in_scopes (parser, bind_arg->scopes,
                              node->info.method_call.on_call_target->info.name.spec_id);
          /* no entity found will be caught as an error later.  Probably an unresolvable target. */
          if (entity)
        {
          node->info.method_call.method_name->info.name.spec_id = entity->info.spec.id;
        }
        }
    }
      break;

    case PT_DATA_TYPE:
      /* don't visit leaves unless this is an object which might contain a name (i.e. CAST(value AS name) ) */
      if (node->type_enum != PT_TYPE_OBJECT)
    {
      *continue_walk = PT_LIST_WALK;
    }
      break;

    case PT_NAME:
      {
    PT_NODE *temp;

    if (node->type_enum == PT_TYPE_MAYBE)
      {
        /* reset spec_id to rebind the name/type */
        node->info.name.spec_id = 0;
      }

    temp = pt_bind_name_or_path_in_scope (parser, bind_arg, node);
    if (temp)
      {
        node = temp;
        /* don't visit leaves */
        *continue_walk = PT_LIST_WALK;
      }
    else
      {
        if (parser->flag.is_parsing_static_sql == 1 && er_errid () == ER_OBJ_INVALID_ATTRIBUTE)
          {
        // clear unknown attribute error, the unknown symbol will be converted (paramterized) to host variable
        er_clear ();
        pt_reset_error (parser);

        node = pt_parameterize_for_static_sql (parser, node);

        /* don't visit leaves */
        *continue_walk = PT_LIST_WALK;
          }
        else
          {
        *continue_walk = PT_STOP_WALK;
          }
      }
      }
      break;

    case PT_DOT_:
      {
    PT_NODE *temp;
    temp = pt_bind_name_or_path_in_scope (parser, bind_arg, node);
    if (temp)
      {
        node = temp;
      }
    else if (PT_CHECK_USER_SCHEMA_PROCEDURE_OR_FUNCTION (node))
      {
        /*
         * when (dot.arg1->node_type == PT_NAME) && (dot.arg2->node_type == PT_FUNCTION),
         * pt_bind_name_or_path_in_scope() always returns NULL and sets the value PT_ERRORmf(.. MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_DEFINED ..).
         */
        pt_reset_error (parser);

        /*
         * jsp_is_exist_stored_procedure() could not be checked in pt_set_user_specified_name(), so it was checked in pt_bind_names().
         * Created a temporary node in name.original to join user_schema(dot.arg1) and sp_name(dot.arg2).
         */
        char downcase_owner_name[DB_MAX_USER_LENGTH];
        downcase_owner_name[0] = '\0';
        char *generic_name = NULL;

        sm_downcase_name (node->info.dot.arg1->info.name.original, downcase_owner_name, DB_MAX_USER_LENGTH);
        generic_name = pt_append_string (parser, downcase_owner_name, ".");
        generic_name = pt_append_string (parser, generic_name, node->info.dot.arg2->info.function.generic_name);
        node->info.dot.arg2->info.function.generic_name = generic_name;
        node->info.dot.arg1->info.name.original = generic_name;

        if (jsp_is_exist_stored_procedure (node->info.dot.arg2->info.function.generic_name))
          {
        node1 = pt_resolve_stored_procedure (parser, node->info.dot.arg2, bind_arg);
        if (node1 == NULL)
          {
            *continue_walk = PT_STOP_WALK;
            return node;
          }
        PT_NODE_COPY_NUMBER_OUTERLINK (node1, node);

        PT_NODE_INIT_OUTERLINK (node);
        parser_free_tree (parser, node);
        node = node1;   /* return the new node */
        /* don't revisit leaves */
        *continue_walk = PT_LIST_WALK;
          }
      }
    else if (pt_has_error (parser))
      {
        return NULL;
      }

    if (!(node->node_type == PT_DOT_
          && (node->info.dot.arg2->node_type == PT_METHOD_CALL || node->info.dot.arg2->node_type == PT_FUNCTION)))
      {
        /* don't revisit leaves */
        *continue_walk = PT_LIST_WALK;
      }

    /* handle dot print format; do not print resolved name for arg2. for example: (CLASS_A, CLASS_B, CLASS_C is
     * class) CLASS_A.b.CLASS_B.c.CLASS_C.name; -> CLASS_A.b.c.name; */
    if (node->node_type == PT_DOT_)
      {
        PT_NODE *arg2;

        for (temp = node; temp->node_type == PT_DOT_; temp = temp->info.dot.arg1)
          {
        /* arg2 is PT_NAME node */
        arg2 = temp->info.dot.arg2;
        if (arg2 && arg2->node_type == PT_NAME)
          {
            arg2->info.name.custom_print |= PT_SUPPRESS_RESOLVED;
          }
          }         /* for (temp = ...) */
      }
      }
      break;

    case PT_FUNCTION:
      if (node->info.function.function_type == PT_GENERIC)
    {
      MOP sp_mop = NULL;
      char sp_unique_name[SM_MAX_IDENTIFIER_LENGTH + 1];
      node->info.function.function_type = pt_find_function_type (node->info.function.generic_name);

      if (node->info.function.function_type == PT_GENERIC)
        {
          if (strchr (node->info.function.generic_name, '.'))
        {
          /*
           * when checking for a PROCEDURE in a PT_DOT_ type, if the PROCEDURE does not exist, the check moves on to the PT_FUNCTION.
           * along the way, it will go through the pt_bind_name_or_path_in_scope() function of PT_NAME,
           * which will always return NULL and set the value of
           * PT_ERRORmf(.. MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_DEFINED ..).
           */
          pt_reset_error (parser);
        }

          /*
           * It may be a method call since they are parsed as
           * nodes PT_FUNCTION.  If so, pt_make_stored_procedure() and pt_make_method_call() will
           * translate it into a method_call.
           */
          sp_mop = jsp_find_stored_procedure (node->info.function.generic_name, DB_AUTH_NONE);
          if (sp_mop != NULL)
        {
          sp_unique_name[0] = '\0';
          jsp_get_unique_name (sp_mop, sp_unique_name, DB_MAX_IDENTIFIER_LENGTH + 1);
          if (sp_unique_name[0] != '\0')
            {
              node->info.function.generic_name = pt_append_string (parser, NULL, sp_unique_name);
            }

          node1 = pt_resolve_stored_procedure (parser, node, bind_arg);
        }
          else
        {
          node1 = pt_resolve_method (parser, node, bind_arg);
        }

          if (node1 == NULL)
        {
          *continue_walk = PT_STOP_WALK;
          return node;
        }

          if (node1->node_type == PT_METHOD_CALL)
        {
          PT_NODE_INIT_OUTERLINK (node);
          parser_free_tree (parser, node);
          node = node1; /* return the new node */
          /* don't revisit leaves */
          *continue_walk = PT_LIST_WALK;
        }
          else
        {
          /* It may be a generic function supported on the server. We put this case last so that user written
           * methods will resolve before trying to make it a server function. */

          if (!pt_type_generic_func (parser, node))
            {
              PT_NODE *top_node = NULL;
              int is_spec_attr = 0;

              /* get top node */
              if (bind_arg != NULL && bind_arg->sc_info != NULL)
            {
              top_node = bind_arg->sc_info->top_node;
            }

              if (top_node != NULL
              && (top_node->node_type == PT_CREATE_INDEX || top_node->node_type == PT_ALTER_INDEX
                  || (top_node->node_type == PT_ALTER && (top_node->info.alter.create_index != NULL))))
            {
              /* check if function name is a spec attribute */
              if (pt_function_name_is_spec_attr (parser, node, bind_arg, &is_spec_attr) != NO_ERROR)
                {
                  return NULL;
                }
            }

              /* show appropriate error message */
              if (is_spec_attr)
            {
              PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC,
                     MSGCAT_SEMANTIC_PREFIX_IN_FUNC_INDX_NOT_ALLOWED);
            }
              else
            {
              char downcase_generic_name[DB_MAX_IDENTIFIER_LENGTH];
              downcase_generic_name[0] = '\0';
              sm_downcase_name (node->info.function.generic_name, downcase_generic_name,
                        DB_MAX_IDENTIFIER_LENGTH);

              if (parser_function_code != PT_EMPTY)
                {
                  PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC,
                      MSGCAT_SEMANTIC_INVALID_INTERNAL_FUNCTION, downcase_generic_name);
                }
              else
                {
                  PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UNKNOWN_FUNCTION,
                      downcase_generic_name);
                }
            }
            }
        }
        }
      else if (node->info.function.function_type < F_TOP_TABLE_FUNC)
        {
          PT_NODE *arg_list = node->info.function.arg_list;

          /* arg list must be a single subquery */

          if (arg_list->next
          || (arg_list->node_type != PT_SELECT && arg_list->node_type != PT_UNION
              && arg_list->node_type != PT_DIFFERENCE && arg_list->node_type != PT_INTERSECTION))
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_WANT_SINGLE_TABLE_IN,
                  pt_short_print (parser, node));
        }
        }

      if (pt_has_error (parser))
        {
          node = NULL;
          *continue_walk = PT_STOP_WALK;
        }
    }
      break;

    case PT_SPEC:
      if (bind_arg->scopes)
    {
      node->info.spec.location = bind_arg->scopes->location++;
    }
      if (node->info.spec.on_cond)
    {
      switch (node->info.spec.join_type)
        {
        case PT_JOIN_INNER:
        case PT_JOIN_LEFT_OUTER:
        case PT_JOIN_RIGHT_OUTER:
          parser_walk_tree (parser, node->info.spec.on_cond, pt_mark_location, &(node->info.spec.location), NULL,
                NULL);
          break;
          /* case PT_JOIN_FULL_OUTER: *//* not supported */

        case PT_JOIN_NONE:
        default:
          break;
        }           /* switch (node->info.spec.join_type) */
      parser_walk_tree (parser, node->info.spec.on_cond, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);
    }
      {
    PT_NODE *entity_name = node->info.spec.entity_name;
    if (entity_name && entity_name->node_type == PT_NAME)
      {
        entity_name->info.name.location = node->info.spec.location;
        if (entity_name->info.name.db_object && db_is_system_class (entity_name->info.name.db_object) > 0)
          {
        bind_arg->sc_info->system_class = true;
          }
      }
      }

      *continue_walk = PT_LIST_WALK;
      break;

    case PT_HOST_VAR:
      pt_bind_type_of_host_var (parser, node);
      break;

    case PT_SCOPE:
      scopestack.specs = node->info.scope.from;
      bind_arg->scopes = &scopestack;
      pt_bind_scope (parser, bind_arg);
      parser_walk_leaves (parser, node, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);

      /* remove this level's scope */
      bind_arg->scopes = bind_arg->scopes->next;

      /* don't revisit leaves */
      *continue_walk = PT_LIST_WALK;
      break;

    case PT_EXPR:
      if (node->info.expr.op == PT_NEXT_VALUE || node->info.expr.op == PT_CURRENT_VALUE)
    {
      /* don't walk leaves */
      *continue_walk = PT_LIST_WALK;
    }
      break;

    case PT_WITH_CLAUSE:
      /* WITH clause should be resolved from SELECT; don't walk leaves(cte_list) */
      *continue_walk = PT_LIST_WALK;
      break;

    case PT_CTE:
      pt_bind_names_in_cte (parser, node, bind_arg);
      if (pt_has_error (parser))
    {
      node = NULL;
      *continue_walk = PT_STOP_WALK;
    }
      else
    {
      /* don't walk leaves */
      *continue_walk = PT_LIST_WALK;
    }
      break;

    default:
      break;
    }

  return node;
}

/*
 * pt_bind_value_to_hostvar_local () -
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in):
 *   continue_walk(in):
 */
static PT_NODE *
pt_bind_value_to_hostvar_local (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  DB_VALUE *value;

  if (node)
    {
      switch (node->node_type)
    {
    case PT_HOST_VAR:
      if (node->info.host_var.var_type != PT_HOST_IN)
        {
          break;
        }

      value = pt_value_to_db (parser, node);
      if (value && prm_get_bool_value (PRM_ID_HOSTVAR_LATE_BINDING))
        {
          /* change PT_NAME to PT_VALUE in order to optimize */
          /* return node ptr */

          PT_NODE *value_node;

          value_node = pt_dbval_to_value (parser, value);
          if (value_node)
        {
          PT_NODE_MOVE_NUMBER_OUTERLINK (value_node, node);

          parser_free_tree (parser, node);

          node = value_node;
        }
        }
      break;
    default:
      break;
    }
    }

  return node;
}               /* pt_bind_value_to_hostvar_local */

/*
 * pt_bind_values_to_hostvars () -
 *   return:
 *   parser(in):
 *   node(in):
 */
PT_NODE *
pt_bind_values_to_hostvars (PARSER_CONTEXT * parser, PT_NODE * node)
{
  if (!pt_has_error (parser))
    {
      node = parser_walk_tree (parser, node, pt_bind_value_to_hostvar_local, NULL, NULL, NULL);
    }

  if (pt_has_error (parser))
    {
      node = NULL;
    }

  return node;
}               /* pt_bind_values_to_hostvars */

/*
 * pt_resolve_default_value () - Fills PT_NAME node with default value
 *
 * return      : error code
 * parser (in) : parser context
 * name (in)   : PT_NAME node
 *
 * NOTE: Filling with default value is forced. After setting default value,
 *   PT_NAME_INFO_FILL_DEFAULT flag is set.
 *   Name must be resolved first.
 */
int
pt_resolve_default_value (PARSER_CONTEXT * parser, PT_NODE * name)
{
  DB_ATTRIBUTE *att = NULL;
  const char *lang_str;
  int flag = 0;
  bool has_user_format;

  if (name->node_type != PT_NAME)
    {
      return NO_ERROR;
    }

  if (name->info.name.meta_class == PT_META_CLASS || name->info.name.meta_class == PT_OID_ATTR)
    {
      return NO_ERROR;
    }

  if (name->info.name.original == NULL || name->info.name.resolved == NULL)
    {
      /* cannot resolve */
      return NO_ERROR;
    }

  if (name->info.name.default_value != NULL)
    {
      /* default value was already set */
      return NO_ERROR;
    }

  att = db_get_attribute_by_name (name->info.name.resolved, name->info.name.original);
  if (att == NULL)
    {
      /* cannot resolve */
      return ER_FAILED;
    }

  if (att->default_value.default_expr.default_expr_type != DB_DEFAULT_NONE)
    {
      /* if the default value is an expression, make a node for it */
      PT_OP_TYPE op;
      PT_NODE *default_op_value_node;

      op = pt_op_type_from_default_expr_type (att->default_value.default_expr.default_expr_type);
      assert (op != (PT_OP_TYPE) 0);
      default_op_value_node = pt_expression_0 (parser, op);

      if (att->default_value.default_expr.default_expr_op == NULL_DEFAULT_EXPRESSION_OPERATOR)
    {
      name->info.name.default_value = default_op_value_node;
    }
      else
    {
      PT_NODE *arg1, *arg2, *arg3;
      arg1 = default_op_value_node;
      has_user_format = att->default_value.default_expr.default_expr_format ? 1 : 0;
      arg2 = pt_make_string_value (parser, att->default_value.default_expr.default_expr_format);
      if (arg2 == NULL)
        {
          parser_free_tree (parser, default_op_value_node);
          PT_ERRORm (parser, name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return ER_FAILED;
        }

      arg3 = parser_new_node (parser, PT_VALUE);
      if (arg3 == NULL)
        {
          parser_free_tree (parser, default_op_value_node);
          parser_free_tree (parser, arg2);
          PT_ERRORm (parser, name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          return ER_FAILED;
        }
      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;

      name->info.name.default_value = parser_make_expression (parser, PT_TO_CHAR, arg1, arg2, arg3);
      if (name->info.name.default_value == NULL)
        {
          parser_free_tree (parser, default_op_value_node);
          parser_free_tree (parser, arg2);
          parser_free_tree (parser, arg3);
          return ER_FAILED;
        }
    }
    }
  else
    {
      /* just set the default value */
      name->info.name.default_value = pt_dbval_to_value (parser, &att->default_value.value);
      if (name->info.name.default_value == NULL)
    {
      PT_ERRORm (parser, name, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      return ER_FAILED;
    }
      if (TP_DOMAIN_TYPE (att->domain) == DB_TYPE_ENUMERATION)
    {
      name->info.name.default_value->data_type = pt_domain_to_data_type (parser, att->domain);
    }
    }

  PT_NAME_INFO_SET_FLAG (name, PT_NAME_INFO_FILL_DEFAULT);
  return NO_ERROR;
}

/*
 * pt_find_attr_in_class_list () - trying to resolve X.attr
 *   return: returns a PT_NAME list or NULL
 *   parser(in):
 *   flat(in): list of PT_NAME nodes (class names)
 *   attr(in): a PT_NAME (an attribute name)
 */
static int
pt_find_attr_in_class_list (PARSER_CONTEXT * parser, PT_NODE * flat, PT_NODE * attr)
{
  DB_ATTRIBUTE *att = 0;
  DB_OBJECT *db = 0;
  PT_NODE *cname = flat;
  const char *lang_str;
  int flag = 0;
  bool has_user_format;

  if (!flat || !attr)
    {
      return 0;
    }

  if (attr->node_type != PT_NAME)
    {
      PT_INTERNAL_ERROR (parser, "resolution");
      return 0;
    }

  /* For Each class name on the list */
  while (cname)
    {
      if (cname->node_type != PT_NAME)
    {
      PT_INTERNAL_ERROR (parser, "resolution");
      return 0;
    }

      /* Get the object */
      db = cname->info.name.db_object;
      if (!db)
    {
      PT_INTERNAL_ERROR (parser, "resolution");
      return 0;
    }

      /* Does db have an attribute named 'name'? */
      att = (DB_ATTRIBUTE *) db_get_attribute_force (db, attr->info.name.original);

      if (!att || attr->info.name.meta_class == PT_META_CLASS || attr->info.name.meta_class == PT_OID_ATTR)
    {
      int db_err;
      db_err = er_errid ();
      if (db_err == ER_AU_SELECT_FAILURE || db_err == ER_AU_AUTHORIZATION_FAILURE)
        {
          PT_ERRORc (parser, attr, er_msg ());
        }
      return 0;
    }

      /* set its type */
      pt_get_attr_data_type (parser, att, attr);

      if (PT_NAME_INFO_IS_FLAGED (attr, PT_NAME_INFO_FILL_DEFAULT))
    {
      if (attr->info.name.default_value != NULL)
        {
          /* default value was already set */
          return 1;
        }
      if (att->default_value.default_expr.default_expr_type != DB_DEFAULT_NONE)
        {
          /* if the default value is an expression, make a node for it */
          PT_OP_TYPE op;
          PT_NODE *default_op_value_node;

          op = pt_op_type_from_default_expr_type (att->default_value.default_expr.default_expr_type);
          assert (op != (PT_OP_TYPE) 0);
          default_op_value_node = pt_expression_0 (parser, op);

          if (att->default_value.default_expr.default_expr_op == NULL_DEFAULT_EXPRESSION_OPERATOR)
        {
          attr->info.name.default_value = default_op_value_node;
        }
          else
        {
          PT_NODE *arg1, *arg2, *arg3;
          arg1 = default_op_value_node;
          has_user_format = att->default_value.default_expr.default_expr_format ? 1 : 0;
          arg2 = pt_make_string_value (parser, att->default_value.default_expr.default_expr_format);
          if (arg2 == NULL)
            {
              parser_free_tree (parser, default_op_value_node);
              PT_ERRORm (parser, attr, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
              return 0;
            }

          arg3 = parser_new_node (parser, PT_VALUE);
          if (arg3 == NULL)
            {
              parser_free_tree (parser, default_op_value_node);
              parser_free_tree (parser, arg2);
              PT_ERRORm (parser, attr, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
              return 0;
            }
          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;

          attr->info.name.default_value = parser_make_expression (parser, PT_TO_CHAR, arg1, arg2, arg3);
          if (attr->info.name.default_value == NULL)
            {
              parser_free_tree (parser, default_op_value_node);
              parser_free_tree (parser, arg2);
              parser_free_tree (parser, arg3);
              PT_ERRORm (parser, attr, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
              return 0;
            }
        }
        }
      else
        {
          /* just set the default value */
          attr->info.name.default_value = pt_dbval_to_value (parser, &att->default_value.value);
          if (attr->info.name.default_value == NULL)
        {
          PT_INTERNAL_ERROR (parser, "resolution");
          return 0;
        }
          if (TP_DOMAIN_TYPE (att->domain) == DB_TYPE_ENUMERATION)
        {
          attr->info.name.default_value->data_type = pt_domain_to_data_type (parser, att->domain);
        }
        }
    }

      cname = cname->next;
    }
  attr->info.name.spec_id = flat->info.name.spec_id;

  return 1;
}

/*
 * pt_find_class_attribute() -  try to resolve attr as a class attribute of cls
 *   return:  1 if all OK, 0 otherwise.
 *   parser(in): the parser context
 *   cls(in): the name of a class
 *   attr(in/out): the name of an attribute
 */
static int
pt_find_class_attribute (PARSER_CONTEXT * parser, PT_NODE * cls, PT_NODE * attr)
{
  DB_ATTRIBUTE *att;
  DB_OBJECT *db;

  if (!parser || !cls || cls->node_type != PT_NAME || !attr || attr->node_type != PT_NAME)
    return 0;

  db = cls->info.name.db_object;

  /* Does db have a class attribute named 'name'? */
  att = (DB_ATTRIBUTE *) db_get_class_attribute (db, attr->info.name.original);

  if (!att)
    {
      int db_err;

      db_err = er_errid ();
      if (!pt_has_error (parser))
    {
      if (db_err == ER_AU_SELECT_FAILURE || db_err == ER_AU_AUTHORIZATION_FAILURE)
        {
          PT_ERRORc (parser, cls, er_msg ());
        }
    }
      return 0;
    }

  attr->info.name.db_object = db;

  /* set its type */
  pt_get_attr_data_type (parser, att, attr);

  /* mark it as resolved */
  attr->info.name.spec_id = cls->info.name.spec_id;
  /* mark it as a class attribute */
  attr->info.name.meta_class = PT_META_ATTR;

  return 1;
}

/*
 * pt_find_name_in_spec () - Given a spec, see if name can be resolved to this
 *   return: 0 if name is NOT an attribute of spec
 *   parser(in):
 *   spec(in):
 *   name(in): a PT_NAME (an attribute name)
 */
static int
pt_find_name_in_spec (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * name)
{
  int ok;
  PT_NODE *col;
  PT_NODE *range_var;
  const char *resolved_name;

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

  if (name->info.name.meta_class == PT_CLASS)
    {
      /* should resolve to a class name later, don't search attributes */
      return 0;
    }

  resolved_name = name->info.name.resolved;
  range_var = spec->info.spec.range_var;
  if (resolved_name && range_var)
    {
      if (pt_str_compare (resolved_name, range_var->info.name.original, CASE_INSENSITIVE) != 0)
    {
      return 0;
    }
    }

  if (PT_SPEC_IS_ENTITY (spec))
    {
      if (spec->info.spec.meta_class == PT_META_CLASS)
    {
      ok = pt_find_class_attribute (parser, spec->info.spec.entity_name, name);
    }
      else
    {
      /* in case of remote server, it always returns true */
      if (spec->info.spec.remote_server_name)
        {
          return 1;
        }
      ok = pt_find_attr_in_class_list (parser, spec->info.spec.flat_entity_list, name);
    }
    }
  else
    {
      assert (PT_SPEC_IS_CTE (spec) || PT_SPEC_IS_DERIVED (spec));
      col = pt_is_on_list (parser, name, spec->info.spec.as_attr_list);
      ok = (col != NULL);
      if (col && !name->info.name.spec_id)
    {
      name->type_enum = col->type_enum;
      if (col->data_type)
        {
          name->data_type = parser_copy_tree_list (parser, col->data_type);
        }
      name->info.name.spec_id = spec->info.spec.id;
      name->info.name.meta_class = PT_NORMAL;

      if (spec->info.spec.derived_table_type == PT_DERIVED_JSON_TABLE)
        {
          // calling default() on any json_table's columns should return NULL
          // set PT_NAME_DEFAULTF_ACCEPTS flag to pass pt_check_defaultf()
          DB_VALUE val;
          db_make_null (&val);
          name->info.name.default_value = pt_dbval_to_value (parser, &val);
          PT_NAME_INFO_SET_FLAG (name, PT_NAME_DEFAULTF_ACCEPTS);
        }
    }
    }

  return ok;
}

/*
 * pt_check_same_datatype() - All DATA_TYPE info fields are compared
 *                            SET_OF() types must match exactly
 *   return:  returns 1 if p & q have the same type
 *   parser(in): the parser context
 *   p(in): any PT_NODE
 *   q(in): any PT_NODE
 *
 * Note :
 * OBJECT types must refer to the same class name.
 * Primitive types have to be IDENTICAL, not just compatible.
 */
int
pt_check_same_datatype (const PARSER_CONTEXT * parser, const PT_NODE * p, const PT_NODE * q)
{
  PT_NODE *s, *t, *v;
  PT_NODE *dt1, *dt2;

  if (!p || !q || !parser)
    {
      return 0;
    }

  /* primitive type match */
  if (p->type_enum != q->type_enum)
    {
      return 0;
    }

  if (p->node_type == PT_DATA_TYPE && q->node_type == PT_DATA_TYPE)
    {
      dt1 = (PT_NODE *) p;
      dt2 = (PT_NODE *) q;
    }
  else
    {
      dt1 = p->data_type;
      dt2 = q->data_type;

      if (dt1 && dt1->node_type != PT_DATA_TYPE)
    {
      return 0;
    }
      if (dt2 && dt2->node_type != PT_DATA_TYPE)
    {
      return 0;
    }
    }

  switch (p->type_enum)
    {
    case PT_TYPE_OBJECT:    /* if type is object, check the names */
      if (dt1 == dt2)
    {
      return 1;
    }

      /* if both are "annonymous" object type, its ok */
      if (dt1 == NULL && dt2 == NULL)
    {
      return 1;
    }

      /* of only one is anonymous its not ok */
      if (dt1 == NULL || dt2 == NULL)
    {
      return 0;
    }

      if (dt1->info.data_type.entity == NULL && dt2->info.data_type.entity == NULL)
    {
      return 1;
    }

      /* Can't mix annonymous and non-annonymous (guards null pointers) */
      if (dt1->info.data_type.entity == NULL || dt2->info.data_type.entity == NULL)
    {
      return 0;
    }

      /* class objects must be the same */
      if (dt1->info.data_type.entity->info.name.db_object != dt2->info.data_type.entity->info.name.db_object)
    {
      return 0;
    }
      break;

    case PT_TYPE_SET:
    case PT_TYPE_MULTISET:
    case PT_TYPE_SEQUENCE:
      s = dt1;
      t = dt2;

      /* if sizes are different, sets can't match */
      if (pt_length_of_list (s) != pt_length_of_list (t))
    {
      return 0;
    }

      /* element types must match */
      while (s)
    {
      v = t;
      while (v)
        {
          if (pt_check_same_datatype (parser, s, v))
        {
          break;    /* got match */
        }
          v = v->next;
        }
      if (!v)
        {
          return 0;     /* s didn't match anything on t */
        }
      s = s->next;
    }
      break;

    default:
      if (dt1 && dt2)
    {
      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
          || dt1->info.data_type.collation_id != dt2->info.data_type.collation_id)
        {
          return 0;
        }
    }
      break;
    }

  return 1;
}

/*
 * pt_check_unique_exposed () - make sure the exposed names in the
 *                              range_var field are all distinct
 *   return: 1 if exposed names are all unique, 0 if duplicate name
 *   parser(in):
 *   p(in): a PT_SPEC node (list)

 * Note :
 * Assumes that the exposed name (range_var) is a PT_NAME node but
 * doesn't check this. Else, crash and burn.
 */
static int
pt_check_unique_exposed (PARSER_CONTEXT * parser, const PT_NODE * p)
{
  PT_NODE *q;

  while (p)
    {
      q = p->next;      /* q = next spec */
      while (q)
    {           /* check that p->range != q->range to the end of list */
      /*
       * Case of comparing names after dot(.).
       * 1. When comparing owner_name.class_name and alias_name.
       *    In Oracle and PostgreSQL, only table_name excluding schema_name or owner_name is compared
       *    with alias_name. In order to operate the same as other DBMSs, only class_name except owner_name
       *    should be compared with alias_name. An error should occur in the case below.
       *    e.g. select t1.c1 from u1.t1, t2 t1;
       *      - exposed_name of "t1"    : "u1.t1"
       *      - exposed_name of "t2 t1" : "t1"
       */
      if (!pt_user_specified_name_compare
          (p->info.spec.range_var->info.name.original, q->info.spec.range_var->info.name.original))
        {
          PT_MISC_TYPE p_type = p->info.spec.range_var->info.name.meta_class;
          PT_MISC_TYPE q_type = q->info.spec.range_var->info.name.meta_class;

          if (p_type != q_type && (p_type == PT_META_CLASS || q_type == PT_META_CLASS))
        {
          /* this happens in statements like: SELECT class t, t.attr FROM t which are rewriten to: SELECT class
           * t, t.attr FROM t, class t In this context, t is different from class t and we should not flag an
           * error */
          q = q->next;  /* check the next one inner loop */
          continue;
        }

          PT_ERRORmf (parser, q, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_AMBIGUOUS_EXPOSED_NM,
              q->info.spec.range_var->info.name.original);
          return 0;
        }

      q = q->next;      /* check the next one inner loop */
    }

      p = p->next;      /* go to next one outer loop */
    }

  return 1;         /* OK */
}

/*
 * pt_check_unique_names () - make sure the spec names are different.
 *
 *   return: 1 if names are all unique, 0 if duplicate name
 *   parser(in):
 *   p(in): a PT_SPEC node (list)

 * Note :
 * If names in range_var are resolved, use pt_check_unique_exposed () instead.
 * This was specially created for DELETE statement which needs to verify
 * that specs have unique names before calling pt_class_pre_fetch ();
 * otherwise it crashes.
 */
int
pt_check_unique_names (PARSER_CONTEXT * parser, const PT_NODE * p)
{
  PT_NODE *q;

  while (p)
    {
      const char *p_name = NULL;
      if (p->node_type != PT_SPEC)
    {
      p = p->next;
      continue;
    }
      if (p->info.spec.range_var && PT_IS_NAME_NODE (p->info.spec.range_var))
    {
      p_name = p->info.spec.range_var->info.name.original;
    }
      else if (p->info.spec.entity_name && PT_IS_NAME_NODE (p->info.spec.entity_name))
    {
      p_name = p->info.spec.entity_name->info.name.original;
    }
      else
    {
      p = p->next;
      continue;
    }
      q = p->next;      /* q = next spec */
      while (q)
    {           /* check that p->range != q->range to the end of list */
      const char *q_name = NULL;
      if (q->node_type != PT_SPEC)
        {
          q = q->next;
          continue;
        }
      if (q->info.spec.range_var && PT_IS_NAME_NODE (q->info.spec.range_var))
        {
          q_name = q->info.spec.range_var->info.name.original;
        }
      else if (q->info.spec.entity_name && PT_IS_NAME_NODE (q->info.spec.entity_name))
        {
          q_name = q->info.spec.entity_name->info.name.original;
        }
      else
        {
          q = q->next;
          continue;
        }
      if (!pt_user_specified_name_compare (p_name, q_name))
        {
          PT_ERRORmf (parser, q, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DUPLICATE_CLASS_OR_ALIAS, q_name);
          return 0;
        }
      q = q->next;      /* check the next one inner loop */
    }
      p = p->next;      /* go to next one outer loop */
    }
  return 1;         /* OK */
}

/*
 * pt_common_attribute () - find the attributes that are identical
 *                          on both lists (i.e. the intersection)
 *   return: returns a modified version of p
 *   parser(in):
 *   p(in/out): a list of PT_NAME nodes representing attributes
 *   q(in): a list of PT_NAME nodes representing attributes
 *
 * Note :
 * This routine is called only from pt_resolve_star() above and should
 * not be used to take 'intersections' of general lists of names since
 * it modifies its argument.
 * WARNING: this routine must maintain the order of p.
 */
static PT_NODE *
pt_common_attribute (PARSER_CONTEXT * parser, PT_NODE * p, PT_NODE * q)
{
  PT_NODE *temp, *result, *w;

  if (!p || !q)
    {
      return 0;
    }
  result = 0;
  while (p)
    {
      /* does the name match something on q? */
      w = pt_is_on_list (parser, p, q);

      /* if so, check that the data_types match */
      if (w && pt_check_same_datatype (parser, p, w))
    {
      /* OK, attach to result */
      temp = p->next;
      p->next = NULL;
      result = parser_append_node (p, result);
      p = temp;
      continue;
    }
      p = p->next;
    }

  return result;
}

/*
 * pt_add_class_to_entity_list () -
 *   return:
 *   parser(in):
 *   class(in):
 *   entity(in):
 *   parent(in):
 *   id(in):
 *   meta_class(in):
 */
PT_NODE *
pt_add_class_to_entity_list (PARSER_CONTEXT * parser, DB_OBJECT * class_, PT_NODE * entity, const PT_NODE * parent,
                 UINTPTR id, PT_MISC_TYPE meta_class)
{
  PT_NODE *flat_list;

  flat_list = pt_make_subclass_list (parser, class_, parent->line_number, parent->column_number, id, meta_class, NULL);
  if (flat_list == NULL)
    {
      return NULL;
    }

  return pt_name_list_union (parser, entity, flat_list);
}

/*
 * pt_domain_to_data_type () - create and return a PT_DATA_TYPE node that
 *                             corresponds to a DB_DOMAIN dom
 *   return: PT_NODE * to a data_type node
 *   parser(in):
 *   domain(in/out):
 *
 * Note : Won't work if type is OBJECT and class name is NULL
 */
PT_NODE *
pt_domain_to_data_type (PARSER_CONTEXT * parser, DB_DOMAIN * domain)
{
  DB_DOMAIN *dom;
  DB_OBJECT *db;
  PT_NODE *result = NULL, *s, *result_last_node = NULL, *entity = NULL;
  PT_TYPE_ENUM t;

  t = pt_db_to_type_enum (TP_DOMAIN_TYPE (domain));
  switch (t)
    {
    case PT_TYPE_JSON:
      result = parser_new_node (parser, PT_DATA_TYPE);
      if (result == NULL)
    {
      return NULL;
    }
      result->type_enum = t;
      if (domain->json_validator != NULL)
    {
      result->info.data_type.json_schema =
        pt_append_bytes (parser, NULL, db_json_get_schema_raw_from_validator (domain->json_validator),
                 (int) strlen (db_json_get_schema_raw_from_validator (domain->json_validator)));
    }
      else
    {
      result->info.data_type.json_schema = NULL;
    }
      break;

    case PT_TYPE_NUMERIC:
    case PT_TYPE_BIT:
    case PT_TYPE_VARBIT:
    case PT_TYPE_CHAR:
    case PT_TYPE_VARCHAR:
      result = parser_new_node (parser, PT_DATA_TYPE);
      if (result == NULL)
    {
      return NULL;
    }
      result->type_enum = t;
      /* some of these types won't have all of the three, but that's okay */
      result->info.data_type.precision = db_domain_precision (domain);
      result->info.data_type.dec_precision = db_domain_scale (domain);
      result->info.data_type.units = db_domain_codeset (domain);
      result->info.data_type.collation_id = db_domain_collation_id (domain);
      result->info.data_type.collation_flag = domain->collation_flag;
      assert (!PT_IS_CHAR_STRING_TYPE (t) || result->info.data_type.collation_id >= 0);
      break;

    case PT_TYPE_OBJECT:
      /* get the object */
      result = parser_new_node (parser, PT_DATA_TYPE);
      if (result == NULL)
    {
      return NULL;
    }
      result->type_enum = t;
      result->info.data_type.entity = NULL;
      result->info.data_type.virt_type_enum = PT_TYPE_OBJECT;
      while (domain)
    {
      db = db_domain_class (domain);
      if (db)
        {
          /* prim_type = PT_TYPE_OBJECT, attach db_object, attach name */
          entity =
        pt_add_class_to_entity_list (parser, db, result->info.data_type.entity, result, (UINTPTR) result,
                         PT_CLASS);
          if (entity == NULL)
        {
          return NULL;
        }
          result->info.data_type.entity = entity;
        }
      domain = (DB_DOMAIN *) db_domain_next (domain);
    }
      break;

    case PT_TYPE_SET:
    case PT_TYPE_SEQUENCE:
    case PT_TYPE_MULTISET:
    case PT_TYPE_MIDXKEY:
      /* set of what? */
      dom = (DB_DOMAIN *) db_domain_set (domain);
      /* make list of types in set */
      while (dom)
    {
      s = pt_domain_to_data_type (parser, dom); /* recursion here */

      if (s)
        {
          if (result)
        {
          /*
           * We want to make sure that the flat name list
           * hanging off of the first PT_DATA_TYPE node is the
           * union of all flat name lists from all nodes in
           * this list; this makes certain things much easier
           * later on.
           * PRESERVE THE ORDER OF THESE LISTS!
           */
          result->info.data_type.entity =
            pt_name_list_union (parser, result->info.data_type.entity, s->info.data_type.entity);
          s->info.data_type.entity = NULL;
          result_last_node->next = s;
          result_last_node = s;
        }
          else
        {
          result = result_last_node = s;
        }
        }

      dom = (DB_DOMAIN *) db_domain_next (dom);
    }
      result_last_node = NULL;

      /*
       * Now run back over the flattened name list and ensure that
       * they all have the same spec id.
       */
      if (result)
    for (s = result->info.data_type.entity; s; s = s->next)
      {
        s->info.name.spec_id = (UINTPTR) result;
      }
      break;

    case PT_TYPE_ENUMERATION:
      {
    DB_ENUM_ELEMENT *db_enum = NULL;
    int idx;

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

    result->type_enum = t;
    result->info.data_type.enumeration = NULL;
    for (idx = 1; idx <= DOM_GET_ENUM_ELEMS_COUNT (domain); idx++)
      {
        db_enum = &DOM_GET_ENUM_ELEM (domain, idx);
        s = pt_make_string_value (parser, DB_GET_ENUM_ELEM_STRING (db_enum));
        DB_SET_ENUM_ELEM_CODESET (&(DB_GET_ENUMERATION (&(s->info.value.db_value))),
                      DB_GET_ENUM_ELEM_CODESET (db_enum));
        if (s == NULL)
          {
        return NULL;
          }
        result->info.data_type.enumeration = parser_append_node (s, result->info.data_type.enumeration);
      }

    result->info.data_type.units = TP_DOMAIN_CODESET (domain);
    result->info.data_type.collation_id = TP_DOMAIN_COLLATION (domain);
    assert (result->info.data_type.collation_id >= 0);
      }
      break;

    default:
      result = parser_new_node (parser, PT_DATA_TYPE);
      if (result == NULL)
    {
      return NULL;
    }
      result->type_enum = t;
      break;
    }

  return result;
}

/*
 * pt_flat_spec_pre () - resolve the entity spec into a flat name list
 *                       and attach it
 *   return:
 *   parser(in):
 *   node(in/out):
 *   chk_parent(in):
 *   continue_walk(in/out):
 */
PT_NODE *
pt_flat_spec_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE *q;
  PT_NODE *result = node;
  PT_FLAT_SPEC_INFO *info = (PT_FLAT_SPEC_INFO *) arg;

  *continue_walk = PT_CONTINUE_WALK;

  if (!node)
    {
      return 0;
    }

  /* if node type is entity_spec(list) process the list */

  switch (node->node_type)
    {
    case PT_INSERT:
    case PT_SELECT:
    case PT_UPDATE:
    case PT_DELETE:
    case PT_GRANT:
    case PT_REVOKE:
    case PT_MERGE:
      info->spec_parent = node;
      break;
    default:
      break;
    }

  if (node->node_type == PT_SPEC)
    {
      /* don't let parser_walk_tree go to rest of list. List is handled here */
      *continue_walk = PT_LEAF_WALK;
      while (node)
    {
      /* if a flat list has not been calculated, calculate it. */
      if (!node->info.spec.flat_entity_list && PT_SPEC_IS_ENTITY (node) && !node->info.spec.remote_server_name)
        {
          /* this sets the persistent entity_spec id. the address of the node may be changed through copying, but
           * this id won't. The number used is the address, just as an easy way to generate a unique number. */
          node->info.spec.id = (UINTPTR) node;

          q = pt_make_flat_name_list (parser, node, info->spec_parent, info->for_update);

          node->info.spec.flat_entity_list = q;
        }

      if (PT_SPEC_IS_ENTITY (node))
        {
          /* entity_spec list are not allowed to have derived column names (for now) */
          if (node->info.spec.as_attr_list)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_WANT_NO_DERIVED_COLS,
                  pt_short_print_l (parser, node->info.spec.as_attr_list));
        }
        }
      else if (PT_SPEC_IS_DERIVED (node))
        {
          PT_NODE *derived_table = node->info.spec.derived_table;
          if (!node->info.spec.id)
        {
          node->info.spec.id = (UINTPTR) node;
        }

          parser_walk_tree (parser, derived_table, pt_flat_spec_pre, info, pt_continue_walk, NULL);
        }
      else
        {
          PT_NODE *cte_def = NULL, *non_recursive_cte = NULL;

          assert (PT_SPEC_IS_CTE (node));
          cte_def = node->info.spec.cte_pointer->info.pointer.node;
          if (cte_def)
        {
          non_recursive_cte = cte_def->info.cte.non_recursive_part;
        }

          if (non_recursive_cte)
        {
          if (!node->info.spec.id)
            {
              node->info.spec.id = (UINTPTR) node;
            }

          parser_walk_tree (parser, non_recursive_cte, pt_flat_spec_pre, info, pt_continue_walk, NULL);
        }
        }

      node = node->next;    /* next item on spec list */
    }

      /* and then do additional checks */
      if (result->node_type == PT_SPEC)
    {
      if (!pt_must_have_exposed_name (parser, result))
        {
          return 0;
        }
      if (!pt_check_unique_exposed (parser, result))
        {
          return 0;
        }
    }
    }

  return result;
}

/*
 * pt_get_all_attributes_and_types() -
 *   return:  cls' list of attributes if all OK, NULL otherwise.
 *   parser(in): handle to parser context
 *   cls(in): a PT_NAME node naming a class in the database
 *   from(in): the entity_spec from which cls was derived
 *             as a flat_entity_list item
 */
static PT_NODE *
pt_get_all_attributes_and_types (PARSER_CONTEXT * parser, PT_NODE * cls, PT_NODE * from)
{
  PT_NODE *result = NULL, *tail, *node;
  DB_ATTRIBUTE *att;
  DB_OBJECT *object;
  bool class_atts_only;

  if (cls == NULL || cls->node_type != PT_NAME || (object = cls->info.name.db_object) == NULL || from == NULL
      || from->node_type != PT_SPEC)
    {
      return NULL;
    }

  class_atts_only = (from->info.spec.meta_class == PT_META_CLASS);
  att = NULL;
  if (class_atts_only)
    {
      att = (DB_ATTRIBUTE *) db_get_class_attributes (object);
    }
  else
    {
      att = (DB_ATTRIBUTE *) db_get_attributes_force (object);
    }

  if (att != NULL)
    {
      /* make result anchor the list */
      result = tail = pt_name (parser, db_attribute_name (att));
      if (result == NULL)
    {
      return NULL;
    }
      result->line_number = from->line_number;
      result->column_number = from->column_number;

      /* set its type */
      pt_get_attr_data_type (parser, att, result);
      if (pt_has_error (parser))
    {
      goto on_error;
    }

      result->info.name.spec_id = from->info.spec.id;
      if (class_atts_only)
    {
      result->info.name.meta_class = PT_META_ATTR;
      result->info.name.db_object = from->info.spec.entity_name->info.name.db_object;
    }

      /* advance to next attribute */
      att = db_attribute_next (att);

      /* for the rest of the attributes do */
      while (att != NULL)
    {
      /* make new node & copy attribute name into it */
      node = pt_name (parser, db_attribute_name (att));
      if (node == NULL)
        {
          goto on_error;
        }
      node->line_number = from->line_number;
      node->column_number = from->column_number;

      /* set its type */
      pt_get_attr_data_type (parser, att, node);
      if (pt_has_error (parser))
        {
          goto on_error;
        }

      node->info.name.spec_id = from->info.spec.id;
      if (class_atts_only)
        {
          node->info.name.meta_class = PT_META_ATTR;
          node->info.name.db_object = from->info.spec.entity_name->info.name.db_object;
        }

      /* append to list */
      tail->next = node;
      tail = node;

      /* advance to next attribute */
      att = db_attribute_next (att);
    }
    }

  return result;

on_error:
  if (result != NULL)
    {
      parser_free_tree (parser, result);
    }

  return NULL;
}

static PT_NODE *
pt_json_table_gather_attribs (PARSER_CONTEXT * parser, PT_NODE * json_table_column, void *args, int *continue_walk)
{
  PT_NODE **attribs = (PT_NODE **) args;

  if (json_table_column->node_type == PT_JSON_TABLE_COLUMN)
    {
      PT_NODE *next_attr = json_table_column->info.json_table_column_info.name;
      next_attr->type_enum = json_table_column->type_enum;
      next_attr->info.name.json_table_column_index = json_table_column->info.json_table_column_info.index;
      if (json_table_column->data_type != NULL)
    {
      next_attr->data_type = parser_copy_tree (parser, json_table_column->data_type);
    }
      *attribs = parser_append_node (next_attr, *attribs);
    }

  return json_table_column;
}

static PT_NODE *
pt_get_all_json_table_attributes_and_types (PARSER_CONTEXT * parser, PT_NODE * json_table_node,
                        const char *json_table_alias)
{
  PT_NODE *attribs = NULL;
  PT_NODE *copy_node = NULL;

  parser_walk_tree (parser, json_table_node, pt_json_table_gather_attribs, &attribs, NULL, NULL);

  if (attribs == NULL)
    {
      assert (false);
      return NULL;
    }

  // *INDENT-OFF*
  std::unordered_map<size_t, PT_NODE *> sorted_attrs;
  // *INDENT-ON*

  for (PT_NODE * attr = attribs; attr; attr = attr->next)
    {
      size_t index = attr->info.name.json_table_column_index;
      assert (strcmp (json_table_alias, attr->info.name.resolved) == 0);

      // we need copies of the actual names
      copy_node = pt_name (parser, attr->info.name.original);
      copy_node->type_enum = attr->type_enum;
      copy_node->info.name.resolved = json_table_alias;
      if (attr->data_type != NULL)
    {
      copy_node->data_type = parser_copy_tree (parser, attr->data_type);
    }
      sorted_attrs[index] = copy_node;  // we have to copy, cannot use same node
    }

  size_t columns_nr = sorted_attrs.size ();

  for (unsigned int i = 0; i < columns_nr - 1; i++)
    {
      if (sorted_attrs[i] == NULL)
    {
      assert (false);
      return NULL;
    }
      sorted_attrs[i]->next = sorted_attrs[i + 1];
    }
  sorted_attrs[columns_nr - 1]->next = NULL;

  return sorted_attrs[0];
}

#define DBLINK_ATTR_NAME      (1)
#define DBLINK_ATTR_TYPE      (2)
#define DBLINK_ATTR_SCALE     (3)
#define DBLINK_ATTR_PRECISION (4)
#define DBLINK_ATTR_CLASS_NAME (11)

PT_TYPE_ENUM pt_type[CCI_U_TYPE_LAST + 1] = {
  PT_TYPE_NULL,
  PT_TYPE_CHAR,
  PT_TYPE_VARCHAR,
  /* TODO:
   * PT_TYPE_NCHAR and PT_TYPE_VARNCHAR will no longer be used(NCHAR was deprecated).
   * CCI_U_TYPE_NCHAR and CCI_U_TYPE_VARNCHAR will no longer be used(NCHAR was deprecated).
   * However, to maintain compatibility with previous versions, the enum list will be preserved.
   */
  PT_TYPE_NULL,         // PT_TYPE_NCHAR  for CCI_U_TYPE_NCHAR
  PT_TYPE_NULL,         // PT_TYPE_VARNCHAR for CCI_U_TYPE_VARNCHAR

  PT_TYPE_BIT,
  PT_TYPE_VARBIT,
  PT_TYPE_NUMERIC,
  PT_TYPE_INTEGER,
  PT_TYPE_SMALLINT,
  PT_TYPE_MONETARY,
  PT_TYPE_FLOAT,
  PT_TYPE_DOUBLE,
  PT_TYPE_DATE,
  PT_TYPE_TIME,
  PT_TYPE_TIMESTAMP,
  PT_TYPE_SET,
  PT_TYPE_MULTISET,
  PT_TYPE_SEQUENCE,
  PT_TYPE_OBJECT,
  PT_TYPE_RESULTSET,
  PT_TYPE_BIGINT,
  PT_TYPE_DATETIME,
  PT_TYPE_BLOB,
  PT_TYPE_CLOB,
  PT_TYPE_ENUMERATION,
  PT_TYPE_SMALLINT,
  PT_TYPE_INTEGER, PT_TYPE_BIGINT, PT_TYPE_TIMESTAMPTZ, PT_TYPE_TIMESTAMPLTZ, PT_TYPE_DATETIMETZ, PT_TYPE_DATETIMELTZ,
  /* Disabled type */
  PT_TYPE_NA,           /* CCI_U_TYPE_TIMETZ, internal only */
  /* end of disabled types */
  PT_TYPE_JSON
};

static T_CCI_U_TYPE
pt_dblink_get_basic_utype (T_CCI_U_EXT_TYPE u_ext_type)
{
  if (CCI_IS_SET_TYPE (u_ext_type))
    {
      return CCI_U_TYPE_SET;
    }
  else if (CCI_IS_MULTISET_TYPE (u_ext_type))
    {
      return CCI_U_TYPE_MULTISET;
    }
  else if (CCI_IS_SEQUENCE_TYPE (u_ext_type))
    {
      return CCI_U_TYPE_SEQUENCE;
    }
  else
    {
      return (T_CCI_U_TYPE) CCI_GET_COLLECTION_DOMAIN (u_ext_type);
    }
}

static bool
pt_dblink_table_fill_attr_def (PARSER_CONTEXT * parser, PT_NODE * attr_def_node, const S_REMOTE_COL_ATTR * attr)
{
  PT_NODE *dt = NULL;
  bool need_precision = true;

  attr_def_node->data_type = NULL;

  if (attr->type_idx > PT_TYPE_NONE)
    {
      /* this is the case of loaddb */
      attr_def_node->type_enum = (PT_TYPE_ENUM) attr->type_idx;
    }
  else
    {
      /* it needs to convert ext type to CCI_U_TYPE */
      attr_def_node->type_enum = pt_type[pt_dblink_get_basic_utype (attr->type_idx)];
    }

  switch (attr_def_node->type_enum)
    {
    case PT_TYPE_JSON:
      break;

    case PT_TYPE_VARCHAR:
    case PT_TYPE_CHAR:
    case PT_TYPE_BIT:
    case PT_TYPE_VARBIT:
    case PT_TYPE_NUMERIC:
    case PT_TYPE_MONETARY:
      break;

    case PT_TYPE_BLOB:
    case PT_TYPE_CLOB:
    case PT_TYPE_OBJECT:
    case PT_TYPE_ENUMERATION:
    case PT_TYPE_SET:
    case PT_TYPE_MULTISET:
    case PT_TYPE_SEQUENCE:
      PT_ERRORmf (parser, attr_def_node, MSGCAT_SET_PARSER_SEMANTIC,
          MSGCAT_SEMANTIC_DBLINK_NOT_SUPPORTED_TYPE, pt_show_type_enum (attr_def_node->type_enum));
      return false;

    default:
      need_precision = false;
      break;
    }

  if (need_precision)
    {
      attr_def_node->data_type = dt = parser_new_node (parser, PT_DATA_TYPE);
      if (dt == NULL)
    {
      PT_ERROR (parser, attr_def_node, er_msg ());
      return false;
    }

      dt->type_enum = attr_def_node->type_enum;

      dt->info.data_type.dec_precision = attr->dec_precision;
      dt->info.data_type.precision = attr->precision;
      dt->info.data_type.units = attr->charset;
    }

  attr_def_node->data_type = dt;
  if (attr_def_node->type_enum == PT_TYPE_CHAR && dt)
    {
      attr_def_node->info.attr_def.size_constraint = dt->info.data_type.precision;
    }

  return true;
}

static PT_NODE *
pt_mk_attr_def_node (PARSER_CONTEXT * parser, PT_NODE * name_node, S_REMOTE_TBL_COLS * rmt_cols)
{
  PT_NODE *def_node;

  def_node = parser_new_node (parser, PT_ATTR_DEF);
  if (!def_node)
    {
      PT_ERROR (parser, name_node, er_msg ());
      return NULL;
    }

  def_node->info.attr_def.attr_name = name_node;
  if (rmt_cols == NULL)
    {
      def_node->data_type = NULL;
      def_node->type_enum = PT_TYPE_INTEGER;
      return def_node;
    }

  const char *col_name = name_node->info.name.original;
  for (int i = 0; i < rmt_cols->get_attr_size (); i++)
    {
      if (intl_identifier_casecmp_for_dblink (col_name, rmt_cols->get_name (i)) == 0)
    {
      if (pt_dblink_table_fill_attr_def (parser, def_node, rmt_cols->get_attr (i)))
        {
          return def_node;
        }
      break;
    }
    }

  parser_free_node (parser, def_node);
  return NULL;
}

static PARSER_VARCHAR *
pt_build_select_list_for_dblink (PARSER_CONTEXT * parser, PT_NODE * col_list)
{
  PARSER_VARCHAR *tvc = 0x00;
  int custom_print_saved = parser->custom_print;

  if (col_list == NULL)
    {               // case: select 1, 'test' from tbl@srv
      return pt_append_bytes (parser, NULL, "1", 1);
    }

  if (col_list->type_enum == PT_TYPE_STAR)
    {               // case:  * or tbl.*
      tvc = pt_append_bytes (parser, NULL, "*", 1);
    }
  else
    {
      parser->custom_print |= PT_SUPPRESS_RESOLVED;
      tvc = pt_print_bytes (parser, col_list);
      if (col_list->next)
    {
      tvc = pt_append_bytes (parser, tvc, ", ", 2);
      tvc = pt_append_varchar (parser, tvc, pt_print_bytes_l (parser, col_list->next));
    }
      parser->custom_print = custom_print_saved;
    }
  return tvc;
}

static void
pt_check_column_list (PARSER_CONTEXT * parser, const char *tbl_alias_nm, PT_DBLINK_INFO * dblink_table,
              S_REMOTE_TBL_COLS * rmt_cols)
{
  if (dblink_table->sel_list == NULL)
    {
      return;
    }

  int i;
  const char *col_name;
  PT_NODE *new_sel_list = NULL;
  PT_NODE *sel_list = dblink_table->sel_list;
  PT_NODE *col;

  while (sel_list)
    {
      col = sel_list;
      sel_list = sel_list->next;
      col->next = NULL;

      if (col->type_enum == PT_TYPE_STAR)
    {           // case:  * or tbl.*
      if (!col->info.name.resolved || intl_identifier_casecmp (tbl_alias_nm, col->info.name.resolved) == 0)
        {
          if (sel_list)
        {
          parser_free_node (parser, sel_list);
        }
          new_sel_list = new_sel_list ? parser_append_node (col, new_sel_list) : col;
          break;
        }
    }

      col_name = col->info.name.original;
      for (i = 0; i < rmt_cols->get_attr_size (); i++)
    {
      if (intl_identifier_casecmp_for_dblink (col_name, rmt_cols->get_name (i)) == 0)
        {
          if (col->info.name.resolved == NULL)
        {
          break;
        }
          else if (intl_identifier_casecmp_for_dblink (tbl_alias_nm, col->info.name.resolved) == 0)
        {
          break;
        }
        }
    }

      if (i < rmt_cols->get_attr_size ())
    {
      new_sel_list = new_sel_list ? parser_append_node (col, new_sel_list) : col;
    }
    }

  dblink_table->sel_list = new_sel_list;
}

static int
pt_remake_dblink_select_list (PARSER_CONTEXT * parser, PT_SPEC_INFO * class_spec, S_REMOTE_TBL_COLS * rmt_cols)
{
  PT_NODE *derived_table = class_spec->derived_table;
  PT_DBLINK_INFO *dblink_table = &derived_table->info.dblink_table;
  PT_NODE *entity_name = dblink_table->qstr;
  PT_NODE *range_var = dblink_table->qstr->next;

  PT_NODE *attr_def_node = NULL;
  PT_NODE *id_node;
  PT_NODE *tmp;

  PARSER_VARCHAR *var_buf = 0;

  int error = NO_ERROR;

  parser->custom_print |= PT_PRINT_SUPPRESS_FOR_DBLINK;

  assert (dblink_table->qstr != NULL);
  assert (class_spec->range_var != NULL);
  assert (class_spec->only_all == PT_ONLY);
  assert (class_spec->except_list == NULL);

  pt_check_column_list (parser, class_spec->range_var->info.name.original, dblink_table, rmt_cols);

  PT_NODE *val = parser_new_node (parser, PT_VALUE);
  if (val == NULL)
    {
      PT_ERROR (parser, derived_table, er_msg ());
      error = ER_DBLINK;
      goto error_exit;
    }

  // select * from dblink_t1@remote_srv1;
  var_buf = pt_append_nulstring (parser, var_buf, "SELECT ");
  var_buf = pt_append_varchar (parser, var_buf, pt_build_select_list_for_dblink (parser, dblink_table->sel_list));

  // from table
  if (class_spec->meta_class == PT_META_CLASS)
    {
      var_buf = pt_append_nulstring (parser, var_buf, " FROM CLASS ");
    }
  else
    {
      var_buf = pt_append_nulstring (parser, var_buf, " FROM ");
    }

  var_buf = pt_append_varchar (parser, var_buf, pt_print_bytes (parser, entity_name));

  // table alias : ~ from tbl@srv t
  if (range_var)
    {
      var_buf = pt_append_bytes (parser, var_buf, " ", 1);
      var_buf = pt_append_varchar (parser, var_buf, pt_print_bytes (parser, range_var));
    }

  val->type_enum = PT_TYPE_CHAR;
  val->info.value.string_type = ' ';
  val->info.value.data_value.str = var_buf;
  PT_NODE_PRINT_VALUE_TO_TEXT (parser, val);
  parser_free_node (parser, dblink_table->qstr);
  dblink_table->qstr = val;

  assert (dblink_table->cols == NULL);

  if (dblink_table->sel_list == NULL)
    {
      PT_NODE *id_node = parser_new_node (parser, PT_NAME);
      if (id_node == NULL)
    {
      error = ER_DBLINK;
      goto error_exit;
    }

      id_node->info.name.original = "c";
      if ((dblink_table->cols = pt_mk_attr_def_node (parser, id_node, NULL)) == NULL)
    {
      error = ER_DBLINK;
    }

      goto error_exit;
    }

  if (dblink_table->sel_list->node_type == PT_NAME && dblink_table->sel_list->type_enum == PT_TYPE_STAR)
    {
      assert (dblink_table->sel_list->next == NULL);
      for (int i = 0; i < rmt_cols->get_attr_size (); i++)
    {
      if ((id_node = parser_new_node (parser, PT_NAME)) == NULL)
        {
          PT_ERROR (parser, derived_table, er_msg ());
          error = ER_DBLINK;
          goto error_exit;
        }

      id_node->info.name.original = pt_append_string (parser, NULL, rmt_cols->get_name (i));
      if ((tmp = pt_mk_attr_def_node (parser, id_node, rmt_cols)) == NULL)
        {
          error = ER_DBLINK;
          goto error_exit;
        }
      attr_def_node = attr_def_node ? parser_append_node (tmp, attr_def_node) : tmp;
    }
    }
  else
    {
      while (dblink_table->sel_list)
    {
      id_node = dblink_table->sel_list;
      dblink_table->sel_list = id_node->next;
      id_node->next = NULL;

      if ((tmp = pt_mk_attr_def_node (parser, id_node, rmt_cols)) == NULL)
        {
          error = ER_DBLINK;
          goto error_exit;
        }
      attr_def_node = attr_def_node ? parser_append_node (tmp, attr_def_node) : tmp;
    }
    }

  dblink_table->cols = attr_def_node;

error_exit:
  parser->custom_print &= ~PT_PRINT_SUPPRESS_FOR_DBLINK;
  return error;
}

#define MAX_LEN_CONNECTION_URL  512
#define SQL_MAX_TEXT_LEN DB_MAX_IDENTIFIER_LENGTH * 2 + 16

static int
pt_dblink_table_get_column_defs (PARSER_CONTEXT * parser, PT_NODE * dblink, S_REMOTE_TBL_COLS * rmt_tbl_cols)
{
  int req = -1, conn = -1, col_cnt, err = ER_DBLINK, i;
  T_CCI_ERROR cci_error;
  T_CCI_COL_INFO *col_info;
  T_CCI_CUBRID_STMT stmt_type;

  PT_DBLINK_INFO *dblink_table = &dblink->info.dblink_table;
  char *find;
  char conn_url[MAX_LEN_CONNECTION_URL] = { 0, };

  char *table_name = dblink_table->remote_table_name;
  char *url = (char *) dblink_table->url->info.value.data_value.str->bytes;
  char *user = (char *) dblink_table->user->info.value.data_value.str->bytes;
  char *passwd = (char *) dblink_table->pwd->info.value.data_value.str->bytes;
  char *sql, *server_name;

  S_REMOTE_COL_ATTR *rmt_attr;

  server_name = (dblink_table->conn) ? (char *) dblink_table->conn->info.name.original : url;

  if (table_name)
    {
      PARSER_VARCHAR *var_buf = 0;

      /* all attr's from "SELECT * FROM" */
      rmt_tbl_cols->set_select_star ();

      /* preparing the query to get the column info */
      sql = pt_append_string (parser, "/* DBLINK SELECT */ SELECT * FROM ", table_name);
    }
  else
    {
      /* in the case of loaddb, it can just check the column from the attr_def node. */
      if (db_client_type_is_loaddb ())
    {
      PT_NODE *cols;

      for (cols = dblink_table->cols; cols; cols = cols->next)
        {
          rmt_attr = rmt_tbl_cols->get_col_attr ((char *) cols->info.attr_def.attr_name->info.name.original);
          rmt_attr->type_idx = cols->type_enum;
          rmt_attr->dec_precision = (cols->data_type) ? cols->info.data_type.dec_precision : 0;
          rmt_attr->precision = (cols->data_type) ? cols->info.data_type.precision : 0;
          rmt_attr->charset = (cols->data_type) ? cols->info.data_type.units : 0;
        }
      return NO_ERROR;
    }

      /* for collecting column info from "SELECT sel-list form dblink(server, 'SELECT ...') */
      sql =
    pt_append_string (parser, "/* DBLINK SELECT */ ",
              (char *) dblink_table->qstr->info.value.data_value.str->bytes);
    }

  find = strstr (url, ":?");

  if (find)
    {
      snprintf (conn_url, MAX_LEN_CONNECTION_URL, "%s%s", url, "&__gateway=true");
    }
  else
    {
      snprintf (conn_url, MAX_LEN_CONNECTION_URL, "%s%s", url, "?__gateway=true");
    }

  conn = cci_connect_with_url_ex (conn_url, user, passwd, &cci_error);
  if (conn < 0)
    {
      goto set_parser_error;
    }

  req = cci_prepare (conn, sql, 0, &cci_error);
  if (req < 0)
    {
      goto set_parser_error;
    }

  col_info = (T_CCI_COL_INFO *) cci_get_result_info (req, &stmt_type, &col_cnt);
  if (col_info == NULL || col_cnt <= 0)
    {
      /* this can not be reached, something wrong */
      sprintf (cci_error.err_msg, "unknown error: cannot fetch the column info from remote server");
      goto set_parser_error;
    }

  for (i = 0; i < col_cnt; i++)
    {
      rmt_attr = rmt_tbl_cols->get_col_attr (col_info[i].col_name);
      rmt_attr->type_idx = col_info[i].ext_type;
      rmt_attr->dec_precision = col_info[i].scale;
      rmt_attr->precision = col_info[i].precision;
      rmt_attr->charset = col_info[i].charset;
    }

  err = NO_ERROR;

set_parser_error:
  if (err < 0)
    {
      if (cci_error.err_msg[0] == '\0')
    {
      sprintf (cci_error.err_msg, "unknown error: there's no error message from remote server");
    }

      if (table_name)
    {
      PT_ERRORf3 (parser, dblink,
              "Failed to get column information for table [%s] on remote [%s]\n%s",
              table_name, server_name, cci_error.err_msg);
    }
      else
    {
      PT_ERRORf3 (parser, dblink,
              "Failed to get column information for query [%s] on remote [%s]\n%s",
              sql, server_name, cci_error.err_msg);
    }
    }

  if (req >= 0)
    {
      cci_close_req_handle (req);
    }

  if (conn >= 0)
    {
      cci_disconnect (conn, &cci_error);
    }

  return err;
}

static PT_NODE *
pt_dblink_table_gather_attribs (PARSER_CONTEXT * parser, PT_NODE * dblink_column, void *args, int *continue_walk)
{
  PT_NODE **attribs = (PT_NODE **) args;

  if (dblink_column->node_type == PT_ATTR_DEF)
    {
      PT_NODE *next_attr = dblink_column->info.attr_def.attr_name;
      next_attr->type_enum = dblink_column->type_enum;

      if (dblink_column->data_type != NULL)
    {
      next_attr->data_type = parser_copy_tree (parser, dblink_column->data_type);
    }
      *attribs = parser_append_node (next_attr, *attribs);
    }
  return dblink_column;
}

static PT_NODE *
pt_get_all_dblink_table_attributes_and_types (PARSER_CONTEXT * parser, PT_NODE * dblink_cols,
                          const char *dblink_table_alias)
{
  PT_NODE *attribs = NULL;

  parser_walk_tree (parser, dblink_cols, pt_dblink_table_gather_attribs, &attribs, NULL, NULL);
  if (attribs == NULL)
    assert (false);

  return attribs;
}

/*
 * pt_get_all_showstmt_attributes_and_types () -
 *   return:  show list attributes list if all OK, NULL otherwise.
 *   parser(in): handle to parser context
 *   derived_table(in): the entity_spec from which cls was derived
 *             as a flat_entity_list item
 */
static PT_NODE *
pt_get_all_showstmt_attributes_and_types (PARSER_CONTEXT * parser, PT_NODE * derived_table)
{
  PT_NODE *result = NULL, *node;
  DB_ATTRIBUTE *att;

  if (derived_table == NULL || derived_table->node_type != PT_SHOWSTMT)
    {
      return NULL;
    }

  att = (DB_ATTRIBUTE *) showstmt_get_attributes (derived_table->info.showstmt.show_type);
  while (att != NULL)
    {
      /* make result anchor the list */
      node = pt_name (parser, db_attribute_name (att));
      if (node == NULL)
    {
      goto on_error;
    }

      /* set its type */
      pt_get_attr_data_type (parser, att, node);

      result = parser_append_node (node, result);

      /* advance to next attribute */
      att = db_attribute_next (att);
    }

  return result;

on_error:
  if (result != NULL)
    {
      parser_free_tree (parser, result);
    }
  return NULL;
}

/*
 * pt_get_attr_data_type () - Given an attribute(att) whose name is in p,
 *      find its data_type and attach the data-type to the name
 *   return:
 *   parser(in):
 *   att(in): a db_attribute
 *   attr(in/out): a PT_NAME node corresponding to att
 *   db(in):
 */
static void
pt_get_attr_data_type (PARSER_CONTEXT * parser, DB_ATTRIBUTE * att, PT_NODE * attr)
{
  DB_DOMAIN *dom;
  if (!attr || !att)
    {
      return;
    }

  dom = db_attribute_domain (att);
  attr->etc = dom;      /* used for getting additional db-specific domain information in the Versant driver */
  attr->type_enum = pt_db_to_type_enum (TP_DOMAIN_TYPE (dom));
  switch (attr->type_enum)
    {
    case PT_TYPE_OBJECT:
    case PT_TYPE_SET:
    case PT_TYPE_SEQUENCE:
    case PT_TYPE_MULTISET:
    case PT_TYPE_NUMERIC:
    case PT_TYPE_BIT:
    case PT_TYPE_VARBIT:
    case PT_TYPE_CHAR:
    case PT_TYPE_VARCHAR:
    case PT_TYPE_ENUMERATION:
    case PT_TYPE_JSON:
      attr->data_type = pt_domain_to_data_type (parser, dom);
      break;
    default:
      break;
    }

  attr->info.name.meta_class = PT_NORMAL;

  /* set its shared attribute flag */
  if (db_attribute_is_shared (att))
    {
      attr->info.name.meta_class = PT_SHARED;
    }
}


/*
 * pt_unwhacked_spec () - Map to the real spec.  Either identity,
 *                        or lookup of selector's path spec.
 *   return:
 *   parser(in):
 *   scope(in):
 *   spec(in):
 */
static PT_NODE *
pt_unwhacked_spec (PARSER_CONTEXT * parser, PT_NODE * scope, PT_NODE * spec)
{
  if (spec && spec->info.spec.derived_table_type == PT_IS_WHACKED_SPEC)
    {
      spec = pt_find_path_entity (parser, scope, spec);
      if (spec)
    {
      spec->info.spec.meta_class = PT_PATH_INNER;
    }
    }
  return spec;
}


/*
 * pt_resolve_correlation () - Given an exposed spec, return the name node
 *     of its oid.
 *   return:
 *   parser(in):
 *   in_node(in):
 *   scope(in):
 *   exposed_spec(in):
 *   col_name(in):
 *   p_entity(in):
 *
 * Note:
 * Also check for some semantic errors
 *    - disallow OIDs of derived spec
 *    - disallow selectors except inside path expression
 */
static PT_NODE *
pt_resolve_correlation (PARSER_CONTEXT * parser, PT_NODE * in_node, PT_NODE * scope, PT_NODE * exposed_spec,
            int col_name, PT_NODE ** p_entity)
{
  PT_NODE *corr_name = NULL;

  exposed_spec = pt_unwhacked_spec (parser, scope, exposed_spec);

  /* If so, name resolves to scope's flat list of entities */
  if (exposed_spec)
    {
      /* the exposed name of a derived table or a CTE may not be used alone, ie, "select e from (select a from c) e"
       * is disallowed. */
      if (col_name && (PT_SPEC_IS_DERIVED (exposed_spec) || PT_SPEC_IS_CTE (exposed_spec))
      && exposed_spec->info.spec.range_var != in_node)
    {
      if (PT_NAME_INFO_IS_FLAGED (in_node, PT_NAME_FOR_UPDATE))
        {
          in_node->info.name.spec_id = exposed_spec->info.spec.id;
          return in_node;
        }
      else
        {
          PT_ERRORmf (parser, in_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_WANT_NO_REF_TO_DRVTB,
              pt_short_print (parser, in_node));
          return NULL;
        }
    }

      /* check for a selector variable in a perverse place */
      if ((in_node->node_type == PT_NAME && in_node->info.name.path_correlation)
      || (in_node->node_type == PT_DOT_
          && ((in_node->info.dot.arg1->node_type == PT_NAME && in_node->info.dot.arg2->info.name.path_correlation)
          || (in_node->info.dot.arg1->node_type == PT_NAME
              && in_node->info.dot.arg1->info.name.path_correlation))))
    {
      PT_ERRORmf (parser, in_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_PATH_CORR_OUTSIDE,
              pt_short_print (parser, in_node));
      return NULL;
    }

      if (!exposed_spec->info.spec.range_var)
    {
      PT_INTERNAL_ERROR (parser, "resolution");
      return NULL;
    }

      if (PT_NAME_INFO_IS_FLAGED (in_node, PT_NAME_FOR_UPDATE))
    {
      corr_name = pt_name (parser, in_node->info.name.original);
      PT_NAME_INFO_SET_FLAG (corr_name, PT_NAME_FOR_UPDATE);
    }
      else
    {
      corr_name = pt_name (parser, "");
    }

      PT_NODE_COPY_NUMBER_OUTERLINK (corr_name, in_node);
      in_node->next = NULL;
      in_node->or_next = NULL;
      if (in_node->info.name.meta_class == PT_META_CLASS)
    {
      corr_name->info.name.meta_class = PT_META_CLASS;
    }
      else
    {
      corr_name->info.name.meta_class = PT_OID_ATTR;
      if (PT_NAME_INFO_IS_FLAGED (in_node, PT_NAME_INFO_GENERATED_OID))
        {
          PT_NAME_INFO_SET_FLAG (corr_name, PT_NAME_INFO_GENERATED_OID);
        }
    }
      if (PT_NAME_INFO_IS_FLAGED (in_node, PT_NAME_ALLOW_REUSABLE_OID))
    {
      PT_NAME_INFO_SET_FLAG (corr_name, PT_NAME_ALLOW_REUSABLE_OID);
    }

      parser_free_tree (parser, in_node);

      corr_name->info.name.spec_id = exposed_spec->info.spec.id;
      corr_name->info.name.resolved = exposed_spec->info.spec.range_var->info.name.original;
      if (PT_IS_SPEC_REAL_TABLE (exposed_spec))
    {
      PT_NAME_INFO_SET_FLAG (corr_name, PT_NAME_DEFAULTF_ACCEPTS);
    }

      /* attach the data type */
      corr_name->type_enum = PT_TYPE_OBJECT;
      if (exposed_spec->info.spec.flat_entity_list)
    {
      corr_name->data_type = pt_object_to_data_type (parser, exposed_spec->info.spec.flat_entity_list);
    }

      *p_entity = exposed_spec;
    }

  return corr_name;
}

/*
 * pt_spec_in_domain() -  determine if cls is in the domain specified by lst
 *   return:  true if cls is in the domain specified by lst
 *   cls(in): a PT_SPEC node representing a class
 *   lst(in): a list of PT_NAME nodes representing a data_type domain
 *
 * Note :
 *   This function is used to check for well-formed selector variables. For
 *   example, given the query
 *     select room[x].size_in_sq_ft
 *     from hotel, (all quarters (except cabin)) x, table(rooms) as t(room)
 *   the selector variable 'x' is well-formed if the exposed spec 'x'
 *   intersects the domain of attribute 'room'.
 */

static int
pt_spec_in_domain (PT_NODE * cls, PT_NODE * lst)
{
  PT_NODE *c;

  if (!cls || cls->node_type != PT_SPEC || !lst)
    {
      return 0;         /* preconditions not met, so nothing doing. */
    }

  while (lst && lst->node_type == PT_NAME)
    {
      /* BEWARE! Do not use and assume that cls->info.spec.entity_name is a PT_NAME node because the code in sq.g
       * class_specification() and elsewhere uses cls->info.spec.entity_name to hold a list of PT_SPEC nodes. Instead,
       * use cls->info.spec.flat_entity_list as the parse tree representation of class_specs of the form 'select ...
       * from (c1 c2 ...) x' */
      c = cls->info.spec.flat_entity_list;

      /* cls is of the form '(c1 c2 ...) x' and we need to determine if any class in the hierarchy of (c1 c2 ...) is a
       * subclass of any class in lst. */
      while (c && c->node_type == PT_NAME && c->info.name.db_object)
    {
      if (c->info.name.db_object == lst->info.name.db_object)
        {
          return 1;     /* cls is a subclass of a class in lst */
        }
      else
        {
          c = c->next;  /* try the next c */
        }
    }
      lst = lst->next;      /* try the next class in lst */
    }
  return 0;         /* cls is not a subclass of any class in lst */
}

/*
 * pt_get_resolution() -  try to resolve a name or path expr using this scope
 *   return:  if in_node is an X.Z with X an exposed name,
 *               collapse it into Z, return Z
 *          if in_node is an X that resolves to scope, return X
 *          if in_node has no resolution in this scope, return NULL
 *   parser(in): the parser context
 *   bind_arg(in): a list of scopes for resolving method calls
 *   scope(in): a list of PT_SPEC nodes (ie, a 'from' clause)
 *   in_node(in/out): an attribute reference or path expression to be resolved
 *   p_entity(out): entity_spec of X if in_node is an X.Z
 *   col_name(in): true on top level call.
 */
static PT_NODE *
pt_get_resolution (PARSER_CONTEXT * parser, PT_BIND_NAMES_ARG * bind_arg, PT_NODE * scope, PT_NODE * in_node,
           PT_NODE ** p_entity, int col_name)
{
  PT_NODE *exposed_spec, *spec, *savespec, *arg1, *arg2, *arg1_name;
  PT_NODE *unique_entity, *path_correlation;
  PT_NODE *temp;
  PT_NODE *reserved_name = NULL;
  PT_FLAT_SPEC_INFO info;

  if (!in_node)
    {
      return NULL;
    }

  if (PT_SHOULD_BIND_RESERVED_NAME (scope))
    {
      /* Attempt to bind to reserved name */
      /* If scope should bind for record information, binding to table attribute is also allowed, so shouldn't stop if
       * binding to reserved names fails. */
      reserved_name = pt_bind_reserved_name (parser, in_node, scope);
      if (!PT_IS_SPEC_FLAG_SET (scope, PT_SPEC_FLAG_RECORD_INFO_SCAN) || reserved_name != NULL)
    {
      return reserved_name;
    }
      /* Couldn't bind to record info, attempt to bind to table attributes */
      /* Fall through */
    }

  if (in_node->node_type == PT_NAME)
    {
      /* Has this name been resolved? */
      if (in_node->info.name.spec_id)
    {
      *p_entity = NULL;
      if (in_node->type_enum == PT_TYPE_OBJECT && in_node->data_type)
        {
          temp = scope;
          while (temp && temp->info.spec.id != in_node->info.name.spec_id)
        {
          temp = temp->next;
        }
          if (temp)
        {
          *p_entity = temp;
          return in_node;
        }
        }
      return NULL;
    }

      /* Let's see if it is a parameter, possibly anchoring a path expr */
      if (in_node->info.name.meta_class == PT_PARAMETER)
    {
      *p_entity = NULL;
      return pt_bind_parameter (parser, in_node);
    }

      if (col_name)
    {
      if (in_node->info.name.path_correlation)
        {
          PT_ERRORmf (parser, in_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_PATH_CORR_OUTSIDE,
              pt_short_print (parser, in_node));
          return NULL;
        }
    }
      else
    {
      /* We are on the left of a dot node. (because we are recursing) Here, correlation names have precedence over
       * column names. (for ANSI compatibility). For unqualified names, column names have precedence. For qualifier
       * names, correlation names have precedence. */
      exposed_spec = pt_is_correlation_name (parser, scope, in_node);
      if (exposed_spec)
        {
          return pt_resolve_correlation (parser, in_node, scope, exposed_spec, col_name, p_entity);
        }
    }

      /* Else, is this an attribute of a unique entity within scope? */
      for (savespec = NULL, spec = scope; spec; spec = spec->next)
    {
      if (pt_find_name_in_spec (parser, spec, in_node))
        {
          if (spec->info.spec.remote_server_name)
        {
          /* only column name with remote server name is resolved
           * PT_DOT node such as new.col obj.old does not resolve */
          if (col_name && spec->info.spec.range_var)
            {
              in_node->info.name.resolved = spec->info.spec.range_var->info.name.original;
              return in_node;
            }
          return NULL;
        }

          if (savespec)
        {
          PT_ERRORmf (parser, in_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_AMBIGUOUS_REF_TO,
                  in_node->info.name.original);
          return NULL;
        }
          savespec = spec;
        }
    }

      if (savespec)
    {
      /* if yes, set the resolution and the resolved name */
      in_node->info.name.resolved = savespec->info.spec.range_var->info.name.original;
      in_node->info.name.partition = savespec->info.spec.range_var->info.name.partition;

      if (PT_IS_SPEC_REAL_TABLE (savespec))
        {
          PT_NAME_INFO_SET_FLAG (in_node, PT_NAME_DEFAULTF_ACCEPTS);
        }

      savespec = pt_unwhacked_spec (parser, scope, savespec);

      *p_entity = savespec;
      return in_node;
    }

      if (col_name)
    {
      /* Failing finding a column name for a name NOT on the left of a dot, try and resolve it as a correlation
       * name (oid). For unqualified names, column names have precedence. For qualifier names, correlation names
       * have precedence. */
      exposed_spec = pt_is_correlation_name (parser, scope, in_node);
      if (exposed_spec)
        {
          return pt_resolve_correlation (parser, in_node, scope, exposed_spec, col_name, p_entity);
        }
    }

      if (in_node->info.name.meta_class == PT_META_CLASS)
    {
      DB_OBJECT *object;
      object = pt_find_users_class (parser, in_node);
      if (object == NULL)
        {
          return NULL;  /* not an valid class */
        }
      in_node->info.name.db_object = object;
      if (bind_arg->spec_frames)
        {
          PT_NODE *class_spec, *node, *entity, *found = NULL;
          for (node = bind_arg->spec_frames->extra_specs; node != NULL; node = node->next)
        {
          if (node->node_type == PT_SPEC && (entity = node->info.spec.entity_name)
              && (entity->info.name.db_object == in_node->info.name.db_object))
            {
              found = node;
              break;
            }
        }

          if (found)
        {
          class_spec = found;
        }
          else
        {
          /* add the new spec to the extra scope */
          if ((class_spec = parser_new_node (parser, PT_SPEC)) == NULL)
            {
              return NULL;
            }
          class_spec->info.spec.id = (UINTPTR) class_spec;
          class_spec->info.spec.only_all = PT_ONLY;
          class_spec->info.spec.meta_class = PT_META_CLASS;
          if ((class_spec->info.spec.entity_name = pt_name (parser, in_node->info.name.original)) == NULL)
            {
              return NULL;
            }
          info.spec_parent = NULL;
          info.for_update = false;
          class_spec = parser_walk_tree (parser, class_spec, pt_flat_spec_pre, &info, pt_continue_walk, NULL);
          bind_arg->spec_frames->extra_specs =
            parser_append_node (class_spec, bind_arg->spec_frames->extra_specs);
        }

          in_node->info.name.meta_class = PT_META_CLASS;
          in_node->info.name.spec_id = class_spec->info.spec.id;
          in_node->info.name.resolved = class_spec->info.spec.range_var->info.name.original;
          PT_NAME_INFO_SET_FLAG (in_node, PT_NAME_DEFAULTF_ACCEPTS);
          /* attach the data type */
          in_node->type_enum = PT_TYPE_OBJECT;
          if (class_spec->info.spec.flat_entity_list)
        {
          in_node->data_type = pt_object_to_data_type (parser, class_spec->info.spec.flat_entity_list);
        }
          *p_entity = class_spec;
          return in_node;
        }
      else
        {
          /* This is an object reference outside of a query */
          if (!in_node->info.name.resolved)
        {
          in_node->info.name.resolved = "";
        }
          in_node->info.name.spec_id = (UINTPTR) in_node->info.name.db_object;
          in_node->info.name.meta_class = PT_META_CLASS;
          in_node->type_enum = PT_TYPE_OBJECT;
          in_node->data_type = parser_new_node (parser, PT_DATA_TYPE);

          if (in_node->data_type == NULL)
        {
          PT_INTERNAL_ERROR (parser, "allocate new node");
          return NULL;
        }

          in_node->data_type->type_enum = PT_TYPE_OBJECT;
          in_node->data_type->info.data_type.entity = parser_copy_tree (parser, in_node);
          in_node->data_type->info.data_type.virt_type_enum = PT_TYPE_OBJECT;
          *p_entity = NULL; /* got no entity! */
          return in_node;
        }
    }

      /* no resolution in this scope */
      return NULL;
    }

  /* Is it a DOT expression X.Z? */
  if (in_node->node_type == PT_DOT_)
    {
      arg1 = in_node->info.dot.arg1;
      arg2 = in_node->info.dot.arg2;
      /* if bad arg2, OR if already resolved, then return same node */
      if (!arg2 || arg2->info.name.spec_id || arg2->node_type != PT_NAME)
    {
      *p_entity = NULL;
      return in_node;
    }

      if (col_name)
    {
      if (arg2->info.name.path_correlation)
        {
          PT_ERRORmf (parser, arg2, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_PATH_CORR_OUTSIDE,
              pt_short_print (parser, in_node));
          return NULL;
        }
    }

      /* Check if this is an exposed name in the current scope. */
      exposed_spec = pt_is_correlation_name (parser, scope, in_node);
      if (exposed_spec)
    {
      return pt_resolve_correlation (parser, in_node, scope, exposed_spec, col_name, p_entity);
    }

      /* if arg1 not in scope, return NULL to indicate not in scope. */
      if (!(arg1 = pt_get_resolution (parser, bind_arg, scope, arg1, p_entity, 0)))
    {
      *p_entity = NULL;
      return NULL;
    }

      if (arg1->node_type == PT_NAME)
    {
      arg1_name = arg1;
    }
      else if (arg1->node_type == PT_METHOD_CALL)
    {
      arg1_name = arg1->info.method_call.method_name;
    }
      else
    {
      arg1_name = arg1->info.dot.arg2;
    }
      /* given X.Z, resolve (X) */
      in_node->info.dot.arg1 = arg1;
      /* Note : this should not get run, now that parameters are evaluated at run time, instead of converted to values
       * at compile time. We need to be careful here.  We may have a path expression anchored by a parameter (PT_VALUE
       * node) or a class attribute object.  If so, we evaluate the path expression here and return the appropriate
       * PT_VALUE node. */
      if (arg1->node_type == PT_VALUE && arg1->type_enum == PT_TYPE_OBJECT)
    {
      temp = pt_eval_value_path (parser, in_node);
      if (!temp)
        {
          return NULL;
        }
      temp->next = in_node->next;
      in_node->next = NULL;
      parser_free_tree (parser, temp);
    }

      if (arg1_name->info.name.meta_class == PT_PARAMETER)
    {
      /* if Parameter was undefined, we would already have raised an error in recursion on arg1 */
      return pt_bind_parameter_path (parser, in_node);
    }

      /* If arg1 is an exposed name, replace expr with resolved arg2 */
      if (arg1->node_type == PT_NAME && (arg1->info.name.meta_class == PT_OID_ATTR))
    {
      /* Arg1 was an exposed name */
      if (pt_find_name_in_spec (parser, *p_entity, arg2))
        {
          /* only mark it resolved if it was found! transfer the info from arg1 to arg2 */
          arg2->info.name.resolved = arg1->info.name.resolved;
          if (PT_NAME_INFO_IS_FLAGED (arg1, PT_NAME_DEFAULTF_ACCEPTS))
        {
          PT_NAME_INFO_SET_FLAG (arg2, PT_NAME_DEFAULTF_ACCEPTS);
        }
          /* don't loose list */
          arg2->next = in_node->next;
          /* save alias */
          arg2->alias_print = in_node->alias_print;
          PT_NAME_INFO_SET_FLAG (arg2, PT_NAME_INFO_DOT_NAME);
          /* replace expr with resolved arg2 */
          in_node->info.dot.arg2 = NULL;
          in_node->next = NULL;
          parser_free_tree (parser, in_node);
          in_node = arg2;
        }
      else
        {
          temp = arg1->data_type;
          if (temp)
        {
          temp = temp->info.data_type.entity;
        }
          if (!temp)
        {
          /* resolution error */
          PT_ERRORmf2 (parser, arg1, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_HAS_NO_ATTR,
                   pt_short_print (parser, arg1), pt_short_print (parser, arg2));
        }
          else if (temp->next)
        {
          PT_ERRORmf2 (parser, arg1, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASSES_HAVE_NO_ATTR,
                   pt_short_print_l (parser, temp), pt_short_print (parser, arg2));
        }
          else
        {
          temp->info.name.resolved = NULL;
          PT_ERRORmf2 (parser, arg1, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_HAVE,
                   pt_short_print_l (parser, temp), pt_short_print (parser, arg2));
        }
          /* signal as not resolved */
          return NULL;
        }
    }
      else if (arg1_name->info.name.meta_class == PT_META_CLASS)
    {
      if (arg1->data_type == NULL)
        {
          return NULL;
        }

      /* Arg1 was a meta class name */
      if (pt_find_class_attribute (parser, arg1->data_type->info.data_type.entity, arg2))
        {
          /* only mark it resolved if it was found! */

          /* A meta class attribute, transfer the class info from arg1 to arg2 */
          arg2->info.name.resolved = arg1->info.name.resolved;
          if (PT_NAME_INFO_IS_FLAGED (arg1, PT_NAME_DEFAULTF_ACCEPTS))
        {
          PT_NAME_INFO_SET_FLAG (arg2, PT_NAME_DEFAULTF_ACCEPTS);
        }
          /* don't lose list */
          arg2->next = in_node->next;
          /* save alias */
          arg2->alias_print = in_node->alias_print;
          PT_NAME_INFO_SET_FLAG (arg2, PT_NAME_INFO_DOT_NAME);
          /* replace expr with resolved arg2 */
          in_node->info.dot.arg2 = NULL;
          in_node->next = NULL;
          parser_free_tree (parser, in_node);
          in_node = arg2;
        }
      else
        {
          temp = arg1->data_type;
          if (temp)
        {
          temp = temp->info.data_type.entity;
        }
          if (!temp)
        {
          /* resolution error */
          PT_ERRORmf2 (parser, arg1, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_HAS_NO_ATTR,
                   pt_short_print (parser, arg1), pt_short_print (parser, arg2));
        }
          else
        {
          temp->info.name.resolved = NULL;
          PT_ERRORmf2 (parser, arg1, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_HAVE,
                   pt_short_print_l (parser, temp), pt_short_print (parser, arg2));
        }
          /* signal as not resolved */
          return NULL;
        }
    }
      else
    {
      /* This is NOT an exposed name, it must be an object attribute. It must also be a legitimate root for this
       * path expression. */
      if (arg1->type_enum != PT_TYPE_OBJECT)
        {
          PT_ERRORmf2 (parser, arg1, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_OBJECT_TYPE,
               pt_short_print (parser, arg1), pt_show_type_enum (arg1->type_enum));
          return NULL;
        }
      else if (!arg1->data_type)
        {
          PT_INTERNAL_ERROR (parser, "Resolution");
          return NULL;
        }
      else if (arg1_name->info.name.path_correlation)
        {
          path_correlation = arg1_name->info.name.path_correlation;
          /* there must be an exposed class spec in this scope */
          exposed_spec = pt_is_correlation_name (parser, scope, path_correlation);
          if (!exposed_spec)
        {
          PT_ERRORmf2 (parser, path_correlation, MSGCAT_SET_PARSER_SEMANTIC,
                   MSGCAT_SEMANTIC_SELECTOR_UNRESOLVED, path_correlation->info.name.original,
                   pt_short_print (parser, scope));
          return NULL;
        }
          else if (exposed_spec->info.spec.derived_table)
        {
          PT_ERRORmf2 (parser, path_correlation, MSGCAT_SET_PARSER_SEMANTIC,
                   MSGCAT_SEMANTIC_SELECTOR_TO_NON_CLS, path_correlation->info.name.original,
                   pt_short_print (parser, exposed_spec));
          return NULL;
        }
          else
        {
          PT_NODE *test = arg1;
          PT_NODE *root_spec;
          PT_NODE *arg1dt;
          while (test->node_type == PT_DOT_)
            {
              test = test->info.dot.arg1;
            }
          if (exposed_spec->info.spec.id == test->info.name.spec_id)
            {
              PT_ERRORmf (parser, path_correlation, MSGCAT_SET_PARSER_SEMANTIC,
                  MSGCAT_SEMANTIC_SELECTOR_DEFINE_SELF, path_correlation->info.name.original);
              return NULL;
            }

          /* test for circularly defined selectors */
          root_spec = scope;
          while (root_spec && (root_spec->info.spec.id != test->info.name.spec_id))
            {
              root_spec = root_spec->next;
            }
          if (root_spec)
            {
              temp = pt_find_entity (parser, exposed_spec->info.spec.path_entities, root_spec->info.spec.id);
              if (temp)
            {
              /* the selectors circularly define each other because this [sel] is the root of the
               * definition of the root of this selector. eg select x.a[y].b, y.a[x].b from x,y */
              PT_ERRORmf2 (parser, path_correlation, MSGCAT_SET_PARSER_SEMANTIC,
                       MSGCAT_SEMANTIC_CYCLIC_SELECTOR, path_correlation->info.name.original,
                       test->info.name.resolved);
              return NULL;
            }

              if (exposed_spec->info.spec.derived_table_type == PT_IS_WHACKED_SPEC)
            {
              /* make sure the exposed spec is already on the root spec path entities. Otherwise we are
               * redefining it. */
              temp = pt_unwhacked_spec (parser, scope, exposed_spec);
              if (temp)
                {
                  temp = temp->info.spec.path_conjuncts;
                  if (temp)
                {
                  temp = temp->info.expr.arg1;
                }
                  if (temp && !pt_name_equal (parser, temp, arg1_name))
                {
                  PT_ERRORmf2 (parser, path_correlation, MSGCAT_SET_PARSER_SEMANTIC,
                           MSGCAT_SEMANTIC_SELECTOR_REDEFINED, path_correlation->info.name.original,
                           pt_short_print (parser, arg1));
                }
                }
            }
            }
          /* make sure the selector variable refers to an entity spec that is a subclass of the arg1->data_type
           * class. */
          if (!pt_has_error (parser) && arg1->data_type && arg1->data_type->node_type == PT_DATA_TYPE
              && (arg1dt = arg1->data_type->info.data_type.entity) && arg1dt->node_type == PT_NAME
              && !pt_spec_in_domain (exposed_spec, arg1dt))
            {
              PT_ERRORmf3 (parser, path_correlation, MSGCAT_SET_PARSER_SEMANTIC,
                   MSGCAT_SEMANTIC_SELECTOR_NOT_SUBCLASS, path_correlation->info.name.original,
                   pt_short_print_l (parser, exposed_spec->info.spec.flat_entity_list),
                   pt_short_print_l (parser, arg1dt));
              return NULL;
            }
        }

          /* no cross product */
          exposed_spec->info.spec.derived_table_type = PT_IS_WHACKED_SPEC;
          parser_free_tree (parser, arg1->data_type->info.data_type.entity);
          arg1->data_type->info.data_type.entity =
        parser_copy_tree_list (parser, exposed_spec->info.spec.flat_entity_list);
          path_correlation->info.name.resolved = path_correlation->info.name.original;
          path_correlation->info.name.original = "";
          path_correlation->info.name.spec_id = exposed_spec->info.spec.id;
          path_correlation->info.name.meta_class = PT_OID_ATTR;
          path_correlation->type_enum = PT_TYPE_OBJECT;
        }
      else
        {
          exposed_spec = NULL;
        }
      if (!pt_find_attr_in_class_list (parser, arg1->data_type->info.data_type.entity, arg2))
        {
          temp = arg1->data_type;
          if (temp)
        {
          temp = temp->info.data_type.entity;
        }
          if (!temp)
        {
          /* resolution error */
          PT_ERRORmf2 (parser, arg1, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_DOM_OBJ_HASNO_ATT_X,
                   pt_short_print (parser, arg1), pt_short_print (parser, arg2));
        }
          else if (temp->next)
        {
          PT_ERRORmf2 (parser, arg1, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASSES_HAVE_NO_ATTR,
                   pt_short_print_l (parser, temp), pt_short_print (parser, arg2));
        }
          else
        {
          temp->info.name.resolved = NULL;
          PT_ERRORmf2 (parser, arg1, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_HAVE,
                   pt_short_print_l (parser, temp), pt_short_print (parser, arg2));
        }
          *p_entity = NULL;
          return NULL;  /* not bound */
        }

      /* we have a good path expression, find entity for arg2 in the path_entities of valid_entity, it may need to
       * be created */

      /* set the type of the dot node to the type of arg2 */
      in_node->type_enum = arg2->type_enum;
      if (arg2->data_type)
        {
          if (in_node->data_type)
        {
          parser_free_tree (parser, in_node->data_type);
        }
          in_node->data_type = parser_copy_tree_list (parser, arg2->data_type);
        }

      /* A normal path must be rooted in an entity spec However, we can have p_entity be NULL for some odd cases.
       * Eg insert into x values(meth(:p1).y) Don't get wigged out here, and it gets handled later. */
      if (*p_entity)
        {
          unique_entity = pt_insert_entity (parser, in_node, *p_entity, exposed_spec);
          *p_entity = unique_entity;
        }
    }
      return in_node;
    }               /* end if-a-dot-expression */

  /* Is it a method call? Be careful, method calls that haven't been resolved masquerade as functions until we call
   * pt_bind_names() on the function node. */
  if ((in_node->node_type == PT_FUNCTION) || (in_node->node_type == PT_METHOD_CALL))
    {
      PT_NODE *new_node;
      if (in_node->node_type == PT_FUNCTION)
    {
      /* call pt_bind_names() to convert the function node into a method node as well as binding its arguments and
       * its target. */
      new_node = parser_walk_tree (parser, in_node, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);
      if (new_node->node_type != PT_METHOD_CALL)
        {
          return NULL;
        }
      else
        {
          /* remove wrong code this cause free memory(i.e., in_node) refer */
          in_node = new_node;
        }
    }
      /* Now, we have a PT_METHOD_CALL_NODE, and the target has been bound. */
      *p_entity = NULL;
      if (in_node->type_enum == PT_TYPE_OBJECT && in_node->data_type)
    {
      temp = scope;
      while (temp && (temp->info.spec.id != in_node->info.method_call.method_name->info.name.spec_id))
        {
          temp = temp->next;
        }
      *p_entity = temp;
      return in_node;
    }
      return NULL;
    }               /* end if PT_FUNCTION || PT_METHOD_CALL */

  /* Else got some node type we shouldn't have */
  PT_INTERNAL_ERROR (parser, "Resolution");
  return NULL;
}

/*
 * pt_expand_external_path() -  expand an attr with a path expression
 *   return:  if in_node is an X.Z with Z a external type, return X.Z.tdata
 *   parser(in): the parser context
 *   in_node(in): an attribute reference or path expression to be resolved
 *   p_entity(out): entity_spec of X if in_node is an X.Z
 */
static PT_NODE *
pt_expand_external_path (PARSER_CONTEXT * parser, PT_NODE * in_node, PT_NODE ** p_entity)
{
#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
  PT_NODE *dot, *domain, *unique_entity;
#endif /* ENABLE_UNUSED_FUNCTION */
  PT_NODE *attr, *entity;
  DB_ATTRIBUTE *attr_obj;

  /* if in_node is a path expr, get last attr in the in_node */
  attr = ((PT_IS_DOT_NODE (in_node)) ? in_node->info.dot.arg2 : in_node);
  if (!PT_IS_NAME_NODE (attr))
    {
      return NULL;
    }

  if (attr->type_enum == PT_TYPE_OBJECT && !PT_NAME_INFO_IS_FLAGED (attr, PT_NAME_INFO_EXTERNAL))
    {
      entity = ((*p_entity) ? (*p_entity)->info.spec.entity_name : NULL);
      if (entity && entity->node_type == PT_NAME && entity->info.name.db_object)
    {
      attr_obj = (DB_ATTRIBUTE *) db_get_attribute_force (entity->info.name.db_object, attr->info.name.original);
#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
      /* check if the last attr is TEXT type */
      if (attr_obj && sm_has_text_domain (attr_obj, 0))
        {
          if ((domain = attr->data_type->info.data_type.entity) == NULL)
        {
          return NULL;
        }
          /* make a path expr like "attr.tdata" */
          if ((dot = parser_new_node (parser, PT_DOT_)) == NULL)
        {
          return NULL;
        }
          dot->info.dot.arg1 = attr;
          if ((dot->info.dot.arg2 = pt_name (parser, "tdata")) == NULL)
        {
          return NULL;
        }
          /* find "tdata" in the domain class, and attach the data type at "tdata" node */
          (void) pt_find_attr_in_class_list (parser, domain, dot->info.dot.arg2);
          dot->type_enum = dot->info.dot.arg2->type_enum;
          if ((dot->data_type = parser_copy_tree_list (parser, dot->info.dot.arg2->data_type)) == NULL)
        {
          return NULL;
        }
          /* insert the domain class into from class list */
          if (*p_entity)
        {
          unique_entity = pt_insert_entity (parser, dot, *p_entity, NULL);
          *p_entity = unique_entity;
        }
          /* merge in_node expr and "attr.tdata" */
          if (PT_IS_DOT_NODE (in_node))
        {
          dot->info.dot.arg1 = in_node->info.dot.arg1;
        }
          PT_NODE_COPY_NUMBER_OUTERLINK (dot, in_node);
          PT_NODE_INIT_OUTERLINK (in_node);
          dot->alias_print = pt_append_string (parser, NULL, attr->info.name.original);
          if (!dot->alias_print)
        {
          return NULL;
        }
          return dot;
        }
#endif /* ENABLE_UNUSED_FUNCTION */
    }
    }

  return in_node;
}

/*
 * pt_is_correlation_name() - checks nam is an exposed name of some
 *                        entity_spec from scope
 *   return:  the entity spec of which nam is the correlation name.
 *      Else 0 if not found or error.
 *   parser(in/out): the parser context
 *   scope(in): a list of PT_SPEC nodes
 *   nam(in): a PT_NAME node
 */

static PT_NODE *
pt_is_correlation_name (PARSER_CONTEXT * parser, PT_NODE * scope, PT_NODE * nam)
{
  PT_NODE *specs;
  PT_NODE *owner = NULL;

  assert (nam != NULL && (nam->node_type == PT_NAME || nam->node_type == PT_DOT_));

  if (nam->node_type == PT_DOT_)
    {
      owner = nam->info.dot.arg1;
      if (owner->node_type != PT_NAME)
    {
      /* could not be owner.correlation */
      return NULL;
    }
      nam = nam->info.dot.arg2;
    }

  if (nam->info.name.meta_class == PT_PARAMETER)
    {
      return NULL;
    }

  for (specs = scope; specs; specs = specs->next)
    {
      if (specs->node_type != PT_SPEC)
    {
      PT_INTERNAL_ERROR (parser, "resolution");
      return NULL;
    }

      if (specs->info.spec.range_var
      && ((nam->info.name.meta_class != PT_META_CLASS) || (specs->info.spec.meta_class == PT_META_CLASS))
      && (pt_user_specified_name_compare (nam->info.name.original, specs->info.spec.range_var->info.name.original)
          == 0))
    {
      if (!owner)
        {
          return specs;
        }
      else
        {
          PT_NODE *entity_name;

          entity_name = specs->info.spec.entity_name;
          if (entity_name && entity_name->node_type == PT_NAME && entity_name->info.name.resolved
          /* actual class ownership test is done for spec no need to repeat that here. */
          && (pt_user_specified_name_compare (entity_name->info.name.resolved, owner->info.name.original) == 0))
        {
          return specs;
        }
        }
    }
    }

  return NULL;
}

/*
 * pt_find_entity () -
 *   return: the entity spec of an entity of a spec in the scope with
 *       an id matching the "match" spec
 *   parser(in): the parser context
 *   scope(in): a list of PT_SPEC nodes
 *   id(in): of a PT_SPEC node
 */
PT_NODE *
pt_find_entity (PARSER_CONTEXT * parser, const PT_NODE * scope, UINTPTR id)
{
  PT_NODE *spec, *path_spec;
  for (spec = (PT_NODE *) scope; spec; spec = spec->next)
    {
      if (spec->node_type != PT_SPEC)
    {
      PT_INTERNAL_ERROR (parser, "resolution");
      return NULL;
    }
      if (spec->info.spec.id == id)
    {
      return spec;
    }
      path_spec = pt_find_entity (parser, spec->info.spec.path_entities, id);
      if (path_spec)
    {
      return path_spec;
    }
    }
  return NULL;
}

/*
 * pt_find_path_entity () -
 *   return: the entity spec of a path entity of a spec in the scope with
 *       an id matching the whacked placeholder spec
 *   parser(in): the parser context
 *   scope(in): a list of PT_SPEC nodes
 *   match(in): a PT_SPEC node that has been "whacked".
 */
static PT_NODE *
pt_find_path_entity (PARSER_CONTEXT * parser, PT_NODE * scope, PT_NODE * match)
{
  PT_NODE *spec, *path_spec;
  UINTPTR id = match->info.spec.id;

  for (spec = scope; spec; spec = spec->next)
    {
      if (spec->node_type != PT_SPEC)
    {
      PT_INTERNAL_ERROR (parser, "resolution");
      return NULL;
    }

      path_spec = pt_find_entity (parser, spec->info.spec.path_entities, id);
      if (path_spec)
    {
      return path_spec;
    }
    }

  return NULL;
}

/*
 * pt_is_on_list () - check whether name node p is equal to
 *                    something on the list
 *   return: Pointer to matching item or NULL
 *   parser(in):
 *   p(in): A PT_NAME node
 *   list(in): A LIST of PT_NAME nodes
 *
 * Note :
 * two strings of length zero match
 * A NULL string does NOT match a zero length string
 */
static PT_NODE *
pt_is_on_list (PARSER_CONTEXT * parser, const PT_NODE * p, const PT_NODE * list)
{
  if (p == NULL)
    {
      return NULL;
    }
  if (p->node_type != PT_NAME)
    {
      return NULL;
    }

  while (list)
    {
      if (list->node_type != PT_NAME)
    {
      PT_INTERNAL_ERROR (parser, "resolution");
      return NULL;      /* this is an error */
    }

      if (pt_user_specified_name_compare (p->info.name.original, list->info.name.original) == 0)
    {
      return (PT_NODE *) list;  /* found a match */
    }
      list = list->next;
    }

  return NULL;          /* no match */
}

/*
 * pt_name_list_union () -
 *   return:
 *   parser(in):
 *   list(in/out): A list of PT_NAME nodes
 *   additions(in): A list of PT_NAME nodes
 *
 * Note :
 *    PT_NAME lists  ( list1 union list2 )
 *    PRESERVING ORDER OF LIST!
 */
static PT_NODE *
pt_name_list_union (PARSER_CONTEXT * parser, PT_NODE * list, PT_NODE * additions)
{
  PT_NODE *result, *temp;

  if (list == NULL)
    {
      return additions;
    }
  if (additions == NULL)
    {
      return list;
    }

  result = list;
  while (additions)
    {
      temp = additions;
      additions = additions->next;
      temp->next = NULL;

      if (!pt_is_on_list (parser, temp, list))
    {
      list = parser_append_node (temp, list);
    }
      else
    {
      parser_free_node (parser, temp);
    }
    }

  return result;
}

/*
 * pt_name_list_diff () -
 *   return: Returns NULL if resulting list is empty
 *   parser(in):
 *   list(in): A list of PT_NAME nodes
 *   deletions(in): A list of PT_NAME nodes
 *
 * Note :
 * PT_NAME lists  (list1 - list2)
 * PRESERVING ORDER OF LIST!
 */
static PT_NODE *
pt_name_list_diff (PARSER_CONTEXT * parser, PT_NODE * list, PT_NODE * deletions)
{
  PT_NODE *result = NULL;
  PT_NODE *temp;

  if (list == NULL)
    {
      return NULL;
    }
  if (deletions == NULL)
    {
      return list;
    }

  while (list)
    {
      temp = list;
      list = list->next;
      temp->next = NULL;

      if (!pt_is_on_list (parser, temp, deletions))
    {
      result = parser_append_node (temp, result);
    }
      else
    {
      parser_free_node (parser, temp);
    }
    }

  parser_free_tree (parser, deletions);

  return result;
}

/*
 * pt_make_flat_name_list () - create a list of its name and all of its
 *                             subclass names, recursively
 *   return: Returns a pointer to (list of) PT_NAME node(s)
 *   parser(in):
 *   db(in): db_object to find subclasses for
 *   line_num(in): input line_num (for error messages)
 *   col_num(in): input column num (for error messages)
 *   id(in):
 *   meta_class(in): parent class for pt_name nodes we create
 *   names_mht(in): memory hash table used to avoid duplicates
 */

static PT_NODE *
pt_make_subclass_list (PARSER_CONTEXT * parser, DB_OBJECT * db, int line_num, int col_num, UINTPTR id,
               PT_MISC_TYPE meta_class, MHT_TABLE * names_mht)
{
  PT_NODE *temp;
  const char *classname;
  PT_NODE *result = 0;      /* will be returned */
  DB_OBJLIST *dbl;      /* list of subclass objects */
  bool ismymht = false;     /* Am I the one who created it? */
  SM_CLASS *smclass;
  int partition_skip;


  if (parser == NULL)
    {
      assert (parser != NULL);
      return NULL;
    }

  /* get the name of THIS class and put it in a PT_NAME node */
  if (db == NULL)
    {
      assert (db != NULL);
      return NULL;
    }

  classname = db_get_class_name (db);
  if (classname == NULL)
    {
      PT_INTERNAL_ERROR (parser, "resolution");
      return NULL;
    }               /* not a class name (error) */

  /* Check to see if this classname is already known, and only add a (name) node if we have never seen it before. Note:
   * Even if we have visited it, we still need to recursively check its subclasses (see dbl below) in order to maintain
   * the correct ordering of classnames found via our depth-first search. */
  if (names_mht == NULL || !mht_get (names_mht, classname))
    {
      result = pt_name (parser, classname);
      if (result == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }
      result->line_number = line_num;
      result->column_number = col_num;
      result->info.name.db_object = db;
      result->info.name.spec_id = id;
      result->info.name.meta_class = meta_class;
      result->info.name.partition = NULL;

      if ((au_fetch_class_force (db, &smclass, AU_FETCH_READ) == NO_ERROR))
    {
      if (smclass->partition != NULL && smclass->partition->pname == NULL)
        {
          result->info.name.partition = smclass->partition;
        }
    }
      else
    {
      PT_ERRORc (parser, result, er_msg ());
      return NULL;
    }

      if (names_mht)
    {
      mht_put (names_mht, classname, (void *) true);
    }
    }

  /* get the list of immediate subclasses of db (may be NULL) */
  dbl = NULL;
  dbl = db_get_subclasses (db);

  /*
   * Build a hash table for all class and subclass names.  This
   * helps us keep from building pt_name nodes for classes we
   * already know about.  Also we only need to build the hash table if
   * there are in fact subclasses of the original class.
   */
  if (names_mht == NULL && dbl)
    {
      names_mht = mht_create ("Pt_Names_Hash_Table", PT_NAMES_HASH_SIZE, mht_4strhash, mht_compare_strings_are_equal);
      if (names_mht != NULL)
    {
      ismymht = true;
      /* Have to stick the first name node created above into the hash */
      mht_put (names_mht, classname, (void *) true);
    }
      else          /* could not create hash table */
    {
      PT_ERRORc (parser, result, er_msg ());
      return NULL;
    }
    }

  /* for each subclass (dbl)...get the list(s) of its subclass names */
  while (dbl)
    {
      partition_skip = 0;

      if (au_fetch_class_force (dbl->op, &smclass, AU_FETCH_READ) == NO_ERROR)
    {
      if (smclass->partition != NULL && smclass->partition->pname != NULL)
        {
          partition_skip = 1;   /* partitioned sub class */
        }
    }
      else
    {
      PT_ERRORc (parser, result, er_msg ());
      result = NULL;
      goto end;
    }

      if (!partition_skip)
    {
      /* here is the recursion */
      temp = pt_make_subclass_list (parser, dbl->op, line_num, col_num, id, meta_class, names_mht);
      /* when names_mht != NULL, it is possible that temp is NULL while no error happen. Such as: diamond problem
       * of multiple inheritance. So, check pt_has_error() is needed. */
      if (temp == NULL && pt_has_error (parser))
        {
          result = NULL;
          goto end;
        }
      /* and attach s to the tail of result. NOTE: ORDER IS IMPORTANT and MUST be maintained.  This used to call
       * pt_name_list_union, but since we only add unique ones, we no longer need to do a set union operation. */
      result = parser_append_node (temp, result);
    }

      /* go to the next subclass */
      dbl = dbl->next;
    }
end:
  /* Delete it only if we created it */
  if (ismymht && names_mht)
    {
      mht_destroy (names_mht);
    }

  return result;
}


/*
 * pt_make_flat_name_list () - Create flat name list from entity spec
 *   return: returns a list of PT_NAME nodes representing the class(es)
 *           referred to by the entity spec
 *   parser(in):
 *   spec(in): A PT_SPEC node representing a single entity spec
 *   spec_parent(in):

 * Note :
 *   Case (A == 'sub-list (A,B,..)'):
 *        Set list(A)= set-theoretic union of the names list(A), list(B), ...
 *        (There is no ONLY or ALL in this case)
 *   Case (A ==  'ONLY X' ||  A == 'X'  (only is implied)):
 *        Set list(A) = the name 'X' which must be an existing class name.
 *        Attach the db_object * to the db_object field in the name node.
 *   Case (A ==  'ALL  X'):
 *        Set list(A) = the name 'X' as above union the names of all
 *            subclasses of X (recursively).
 *   Case (A == 'ALL  X EXCEPT Y'):
 *        Set list(A) = list(ALL X) - list(Y)
 *                (set-theoretic difference of the lists)
 *        Additionally:
 *               list(Y) must be a subset of list(X), else error.
 *               list(X)-list(Y)  must be non-empty, else error.
 */
static PT_NODE *
pt_make_flat_name_list (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * spec_parent, bool for_update)
{
  PT_NODE *result = 0;      /* the list of names to return */
  PT_NODE *temp, *temp1, *temp2, *name;
  DB_OBJECT *db;        /* a temp for class object */
  const char *class_name = NULL;    /* a temp to extract name from class */
  const char *obj_class_name = NULL;
  PT_NODE *e_node;
  DB_AUTH type;
  AU_FETCHMODE fetchmode;

  if (!spec)
    {
      return 0;
    }
  if (spec->node_type != PT_SPEC)
    {
      PT_INTERNAL_ERROR (parser, "resolution");
      return NULL;
    }

  if (spec->info.spec.partition != NULL)
    {
      spec = pt_resolve_partition_spec (parser, spec, spec_parent, for_update);
      if (spec == NULL)
    {
      return spec;
    }
      return spec->info.spec.flat_entity_list;
    }

  name = spec->info.spec.entity_name;
  if (name == 0)
    {
      /* is a derived table */
      PT_INTERNAL_ERROR (parser, "resolution");
      return NULL;
    }               /* internal error */

  /* If name field points to a name node (i.e. is not a sublist ) then .. */
  if (name->node_type == PT_NAME)
    {
      DB_OBJECT *classop;
      SM_CLASS *class_;

      class_name = name->info.name.original;
      classop = db_find_class_with_purpose (class_name, for_update);
      if (classop != NULL)
    {
      if (for_update)
        {
          fetchmode = AU_FETCH_UPDATE;
          type = AU_ALTER;
        }
      else
        {
          fetchmode = AU_FETCH_READ;
          type = AU_SELECT;
        }

      if (au_fetch_class (classop, &class_, fetchmode, type) == NO_ERROR)
        {
          /* 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)
        {
          if (intl_identifier_casecmp (class_name, class_->header.ch_name) != 0)
            {
              name->info.name.original = pt_append_string (parser, NULL, class_->header.ch_name);
            }
        }

          if (class_->partition != NULL)
        {
          if (class_->partition->pname == NULL)
            {
              /* parent partition class */
              name->info.name.partition = class_->partition;
            }
          else
            {
              if (spec_parent && spec_parent->node_type != PT_SELECT
              && spec_parent->node_type != PT_CREATE_INDEX && spec_parent->node_type != PT_DROP_INDEX
              && spec_parent->node_type != PT_ALTER_INDEX && spec_parent->node_type != PT_MERGE
              && spec_parent->node_type != PT_DELETE && spec_parent->node_type != PT_UPDATE
              && spec_parent->node_type != PT_INSERT)
            {
              /* partition not allowed */
              PT_ERRORm (parser, spec, MSGCAT_SET_PARSER_SEMANTIC,
                     MSGCAT_SEMANTIC_INVALID_PARTITION_REQUEST);
              return NULL;
            }
            }
        }
        }
    }
      else
    {
      PT_ERRORc (parser, spec, db_error_string (3));
      return NULL;
    }

      /* if ONLY... */
      if (spec->info.spec.only_all == PT_ONLY)
    {
      /* Get the name */
      name->info.name.spec_id = spec->info.spec.id;
      name->info.name.meta_class = spec->info.spec.meta_class;
      /* Make sure this is the name of a class */
      db = pt_find_users_class (parser, name);
      name->info.name.db_object = db;
      if (!db)
        {
          return 0;     /* error already set */
        }
      /* an error. Isn't a class name */
      /* create a new name node with this class name on it. Return it. */
      result = pt_name (parser, class_name);
      result->line_number = spec->line_number;
      result->column_number = spec->column_number;
      result->info.name.db_object = db;
      result->info.name.spec_id = spec->info.spec.id;
      result->info.name.meta_class = spec->info.spec.meta_class;
      result->info.name.partition = name->info.name.partition;
      return result;    /* there can be no except part */
    }

      /* if ALL... */
      if (spec->info.spec.only_all == PT_ALL)
    {
      /* get the entity_name as a string */
      name->info.name.spec_id = spec->info.spec.id;
      name->info.name.meta_class = spec->info.spec.meta_class;
      /* get the DB_OBJECT of which it is the name */
      db = pt_find_users_class (parser, name);
      name->info.name.db_object = db;
      if (!db)
        {
          return 0;
        }
      /* get list of this class name and all its subclass names */
      temp =
        pt_make_subclass_list (parser, db, spec->line_number, spec->column_number, spec->info.spec.id,
                   spec->info.spec.meta_class, NULL);
      if (temp == NULL)
        {
          return NULL;
        }
      /* do the EXCEPT part if present */
      if (spec->info.spec.except_list == 0)
        {
          temp1 = 0;
        }
      else
        {
          temp1 = NULL;
          for (e_node = spec->info.spec.except_list; e_node; e_node = e_node->next)
        {
          /* recursion */
          temp2 = pt_make_flat_name_list (parser, e_node, spec_parent, false);
          if (!temp2)
            {
              return NULL;
            }
          temp1 = parser_append_node (temp2, temp1);
        }
        }

      /* check that each item on the EXCEPT flat-list is, in fact, a subclass name that is on the 'ALL' flat-list */
      temp2 = temp1;
      while (temp2)
        {
          if (!pt_is_on_list (parser, temp2, temp))
        {
          PT_ERRORmf (parser, temp2, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_EXCEPTSPEC_NOT_HEIR,
                  temp2->info.name.original);
          return 0;
        }
          temp2 = temp2->next;
        }

      /* return the difference of the two lists */
      result = pt_name_list_diff (parser, temp, temp1);
      if (!result)
        {
          PT_ERRORm (parser, temp, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SPEC_EXCLUDES_ALL);
        }
      return result;
    }
      /* end if ALL */

      /* parser internal error, wasn't ONLY or ALL */
      PT_INTERNAL_ERROR (parser, "resolution");
      return 0;
    }
  /* end if not a sublist */

  /* If name field->ENTITY_SPEC node then we are dealing with a sublist: ( all A, B, all C except D, ...) There is no
   * ONLY/ALL/EXCEPT part for the sub-list as a whole. We take the union of all the names on the sub-list. */
  if (name->node_type == PT_SPEC)
    {
      result = 0;
      temp = name;
      /* for each (sub)entity_spec */
      while (temp)
    {           /* recursion here */
      temp1 = pt_make_flat_name_list (parser, temp, spec_parent, for_update);
      result = pt_name_list_union (parser, result, temp1);
      temp = temp->next;
    }

      temp = result;
      while (temp)
    {
      temp->info.name.spec_id = spec->info.spec.id;
      temp->info.name.meta_class = spec->info.spec.meta_class;
      temp = temp->next;
    }

      return result;
    }

  PT_INTERNAL_ERROR (parser, "resolution");
  return 0;         /* internal error, wasn't a name or a sublist */
}


/*
 * pt_must_have_exposed_name () - MUST assign a name (even a default one)
 *      because later checks assume the range_var field is non-empty
 *   return: 0 if error or can't assign a name. 1 if successful
 *   parser(in):
 *   p(in): a PT_SPEC node (list)
 *
 * Note :
 *      For each item on the entity_spec list:
 *        if not an entity spec, return 0
 *        if .range_var already assigned, continue
 *        if .range_var can be given the default (only class name) do it.
 *        if can't assign name, return 0
 *      Return 1 if every spec on list has a .range_var or can be given one
 *         Set the .resolved field in each item in the flat list to
 *         the corresponding exposed name. This is because in a query like:
 *            select x.name,y.name from p x, p y  both entity spec lists
 *         generate the same flat list. We want to be able to tell them
 *         apart later when we use them.
 */

static int
pt_must_have_exposed_name (PARSER_CONTEXT * parser, PT_NODE * p)
{
  PT_NODE *q = 0, *r;
  PT_NODE *spec_first = p;

  while (p)
    {
      if (p->node_type == PT_SPEC)
    {
      /* if needs a name */
      if (p->info.spec.range_var == NULL)
        {
          q = p->info.spec.entity_name;
          /* if an exposed name is not given, then exposed name is itself. */
          if (q && q->node_type == PT_NAME)
        {       /* not a sub list */
          q->info.name.spec_id = p->info.spec.id;
          q->info.name.meta_class = p->info.spec.meta_class;
          p->info.spec.range_var = parser_copy_tree (parser, q);
          p->info.spec.range_var->info.name.resolved = NULL;
        }
          else if (p->info.spec.cte_name)
        {
          /* The name of the spec should be the name of the CTE */
          p->info.spec.range_var = parser_copy_tree (parser, p->info.spec.cte_name);
        }
          else
        {
          const char *unique_exposed_name;
          /*
           * Was sublist, they didn't give a correlation variable name so We generate a unique name and attach
           * it. */
          r = parser_new_node (parser, PT_NAME);

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

          r->info.name.spec_id = p->info.spec.id;
          r->info.name.meta_class = p->info.spec.meta_class;

          unique_exposed_name = pt_get_unique_exposed_name (parser, spec_first);

          if (unique_exposed_name == NULL)
            {
              PT_INTERNAL_ERROR (parser, "allocate new table name");
              return 0;
            }

          r->info.name.original = unique_exposed_name;
          r->line_number = p->line_number;
          r->column_number = p->column_number;
          p->info.spec.range_var = r;
        }
        }
      p->info.spec.range_var->info.name.meta_class = p->info.spec.meta_class;
      /* If we get here, the item has a name. Copy name-pointer to the resolved field of each item on flat entity
       * list */
      q = p->info.spec.flat_entity_list;
      while (q)
        {
          q->info.name.resolved = p->info.spec.range_var->info.name.original;
          if (PT_IS_SPEC_REAL_TABLE (p))
        {
          PT_NAME_INFO_SET_FLAG (q, PT_NAME_DEFAULTF_ACCEPTS);
        }
          q = q->next;
        }
      p = p->next;
    }
    }               /* continue while() */

  return 1;
}


/*
 * pt_object_to_data_type () - create a PT_DATA_TYPE node that corresponds
 *                             to it and return it
 *   return: PT_NODE * to a data_type node
 *   parser(in):
 *   class_list(in):
 */
static PT_NODE *
pt_object_to_data_type (PARSER_CONTEXT * parser, PT_NODE * class_list)
{
  PT_NODE *result;
  result = parser_new_node (parser, PT_DATA_TYPE);
  if (result == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

  result->type_enum = PT_TYPE_OBJECT;
  result->info.data_type.entity = parser_copy_tree_list (parser, class_list);
  result->info.data_type.virt_type_enum = PT_TYPE_OBJECT;
  result->line_number = class_list->line_number;
  result->column_number = class_list->column_number;
  return result;
}

/*
 * pt_resolve_star_reserved_names () - Resolves '*' value in select list
 *                     when a hint that activates reserved
 *                     names is used.
 *
 * return      : List of reserved names.
 * parser (in) : Parser context.
 * from (in)   : Query spec.
 *
 * NOTE: The reserved names depend on the scan type flag on from node.
 */
static PT_NODE *
pt_resolve_star_reserved_names (PARSER_CONTEXT * parser, PT_NODE * from)
{
  PT_NODE *reserved_names = NULL;
  PT_NODE *new_name = NULL;
  int i, start, end;
  PT_RESERVED_NAME_TYPE reserved_name_type;

  if (parser == NULL || from == NULL || from->node_type != PT_SPEC)
    {
      assert (0);
      return NULL;
    }

  reserved_name_type = PT_SPEC_GET_RESERVED_NAME_TYPE (from);
  if (reserved_name_type == RESERVED_NAME_INVALID)
    {
      assert (0);
      return NULL;
    }

  PT_GET_RESERVED_NAME_FIRST_AND_LAST (reserved_name_type, start, end);

  for (i = start; i <= end; i++)
    {
      /* create a new node for each reserved name */
      new_name = pt_name (parser, pt_Reserved_name_table[i].name);
      if (new_name == NULL)
    {
      PT_ERRORm (parser, from, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      parser_free_tree (parser, reserved_names);
      return NULL;
    }
      /* mark the node as reserved */
      new_name->info.name.meta_class = PT_RESERVED;
      new_name->info.name.reserved_id = pt_Reserved_name_table[i].id;
      /* resolve name */
      new_name->info.name.spec_id = from->info.spec.id;
      new_name->info.name.resolved = from->info.spec.range_var->info.name.original;
      /* set type enum to the expected type */
      new_name->type_enum = pt_db_to_type_enum (pt_Reserved_name_table[i].type);
      if (new_name->type_enum == PT_TYPE_OBJECT)
    {
      new_name->data_type =
        pt_domain_to_data_type (parser,
                    tp_domain_resolve (pt_Reserved_name_table[i].type,
                               from->info.spec.entity_name->info.name.db_object, 0, 0, NULL,
                               0));
    }
      /* append to name list */
      reserved_names = parser_append_node (new_name, reserved_names);
    }
  /* return reserved name list */
  return reserved_names;
}

/*
 * pt_resolve_star () - resolve the '*' as in a query
 *      Replace the star with an equivalent list x.a, x.b, y.a, y.d ...
 *   return:
 *   parser(in):
 *   from(in): a PT_SELECT node
 *   attr(in): NULL if "*", non-NULL if "class_name.*"
 *
 * Note :
 * ASSUMES
 *    Flat entity lists in the 'from' clause have already been created.
 *    Items in from list all have or have been given an exposed name.
 */
PT_NODE *
pt_resolve_star (PARSER_CONTEXT * parser, PT_NODE * from, PT_NODE * attr)
{
  PT_NODE *flat_list;
  PT_NODE *flat, *spec_att, *class_att, *attr_name, *range, *result = NULL;
  PT_NODE *spec = from;

  if (PT_SHOULD_BIND_RESERVED_NAME (from))
    {
      return pt_resolve_star_reserved_names (parser, from);
    }

  while (spec)
    {
      if (attr)
    {           /* resolve "class_name.*" */
      if (attr->info.name.spec_id != spec->info.spec.id)
        {
          spec = spec->next;    /* skip to next spec */
          continue;
        }
    }

      /* spec_att := all attributes of this entity spec */
      spec_att = NULL;

      if (PT_SPEC_IS_DERIVED (spec) || PT_SPEC_IS_CTE (spec))
    {
      spec_att = parser_copy_tree_list (parser, spec->info.spec.as_attr_list);
    }
      else
    {
      /* spec_att := intersection of all attributes of spec's subclasses */
      assert (PT_SPEC_IS_ENTITY (spec));
      flat_list = spec->info.spec.flat_entity_list;
      for (flat = flat_list; flat; flat = flat->next)
        {
          /* get attribute list for this class flat */
          class_att = pt_get_all_attributes_and_types (parser, flat, spec);
          /* take intersection except first time */
          spec_att = ((flat == flat_list) ? class_att : pt_common_attribute (parser, spec_att, class_att));

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

      attr_name = spec_att;
      range = spec->info.spec.range_var;
      while (attr_name)
    {
      if (range)
        {
          attr_name->info.name.resolved = range->info.name.original;
        }

      PT_NAME_INFO_SET_FLAG (attr_name, (attr) ? PT_NAME_INFO_DOT_STAR : PT_NAME_INFO_STAR);
      /* expand "attr" into "attr.tdata" if the attr is TEXT */
      if (attr_name->type_enum == PT_TYPE_OBJECT && !PT_NAME_INFO_IS_FLAGED (attr_name, PT_NAME_INFO_EXTERNAL))
        {
          DB_ATTRIBUTE *att = NULL;
          PT_NODE *entity_name = spec->info.spec.entity_name;
          if (entity_name && entity_name->node_type == PT_NAME && entity_name->info.name.db_object)
        {
          att = db_get_attribute (entity_name->info.name.db_object, attr_name->info.name.original);
        }
#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
          if (att && sm_has_text_domain (att, 0))
        {
          PT_NODE *prev_entity;
          PT_NODE *save_next = attr_name->next;
          PT_NODE *dot_arg1 = parser_copy_tree (parser, attr_name);
          PT_NODE *dot_arg2 = pt_name (parser, "tdata");
          if (!dot_arg1 || !dot_arg2)
            {
              return NULL;
            }
          else
            {
              parser_reinit_node (attr_name);
              attr_name->node_type = PT_DOT_;
              attr_name->info.dot.arg1 = dot_arg1;
              attr_name->info.dot.arg2 = dot_arg2;
              attr_name->alias_print = pt_append_string (parser, NULL, dot_arg1->info.name.original);
              if (!attr_name->alias_print)
            {
              return NULL;
            }
              attr_name = pt_get_resolution (parser, NULL, spec, attr_name, &prev_entity, 1);
              if (!attr_name)
            {
              return NULL;
            }
              attr_name->next = save_next;
            }
        }
#endif /* ENABLE_UNUSED_FUNCTION */
        }
      attr_name = attr_name->next;
    }

      if (result)
    {
      /* attach spec_att to end of result */
      attr_name = result;
      while (attr_name->next)
        {
          attr_name = attr_name->next;
        }
      attr_name->next = spec_att;
    }
      else
    {
      result = spec_att;
    }

      if (attr)
    {
      break;
    }
      spec = spec->next;
    }

  return result;
}

/*
 * pt_resolve_vclass_args () - modifies the attribute list in the insert
 *    statement by adding to the specified attributes the ones missing with
 *    their default values (if not null). is applied only to views.
 *
 *    return: the modified statement
 *    parser(in):
 *    statement(in): insert statement
 *
 * NOTE: this step is needed because all information on the view is lost after
 *   translate (including default values for the missing attributes).
 */
static PT_NODE *
pt_resolve_vclass_args (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  PT_NODE *spec = NULL;
  PT_NODE *entity_name = NULL;
  PT_NODE *attr_list = NULL, *attr = NULL;
  PT_NODE *value_clauses = NULL, *value_list = NULL;
  PT_NODE *crt_node = NULL;
  PT_NODE *rest_attrs = NULL, *rest_values = NULL;
  DB_OBJECT *db_obj = NULL;
  SM_ATTRIBUTE *db_attributes = NULL, *db_attr = NULL;
  int is_values, is_vclass = 0;
  int is_subqery;

  if (!statement || statement->node_type != PT_INSERT)
    {
      /* do nothing */
      return statement;
    }

  spec = statement->info.insert.spec;
  entity_name = spec->info.spec.entity_name;
  if (!entity_name || entity_name->node_type != PT_NAME)
    {
      /* enitity_name should be resolved before going further */
      return statement;
    }
  db_obj = entity_name->info.name.db_object;
  if (!db_obj)
    {
      /* this applies only for views */
      return statement;
    }
  is_vclass = db_is_vclass (db_obj);
  if (is_vclass < 0)
    {
      PT_ERRORc (parser, statement, er_msg ());
      goto error;
    }
  if (!is_vclass)
    {
      return statement;
    }

  value_clauses = statement->info.insert.value_clauses;
  value_list = value_clauses->info.node_list.list;
  attr_list = statement->info.insert.attr_list;
  if (!attr_list)
    {
      return statement;
    }

  is_values = (value_clauses->info.node_list.list_type == PT_IS_VALUE);
  is_subqery = (value_clauses->info.node_list.list_type == PT_IS_SUBQUERY);

  db_attributes = db_get_attributes_force (db_obj);
  if (db_attributes == NULL)
    {
      return statement;
    }

  rest_attrs = NULL;
  rest_values = NULL;

  for (db_attr = db_attributes; db_attr; db_attr = db_attribute_next (db_attr))
    {
      const char *name = db_attr->header.name;

      if (db_attr->default_value.default_expr.default_expr_type == DB_DEFAULT_NONE
      && DB_IS_NULL (&db_attr->default_value.value))
    {
      continue;
    }

      for (attr = attr_list; attr; attr = attr->next)
    {
      if (pt_str_compare (name, attr->info.name.original, CASE_INSENSITIVE) == 0)
        {
          break;
        }
    }
      if (!attr)
    {
      /* create attribute & default value and update rest_lists */
      attr = parser_new_node (parser, PT_NAME);
      if (!attr)
        {
          PT_ERROR (parser, statement, "allocation error");
          goto error;
        }
      attr->info.name.original = pt_append_string (parser, NULL, name);
      if (attr->info.name.original == NULL)
        {
          PT_ERRORc (parser, statement, er_msg ());
          goto error;
        }
      if (rest_attrs)
        {
          rest_attrs = parser_append_node (attr, rest_attrs);
        }
      else
        {
          rest_attrs = attr;
        }
      if (is_values)
        {
          PT_NODE *val = parser_new_node (parser, PT_EXPR);
          if (!val)
        {
          PT_ERROR (parser, statement, "allocation error");
          goto error;
        }
          val->info.expr.op = PT_DEFAULTF;
          if (rest_values)
        {
          rest_values = parser_append_node (val, rest_values);
        }
          else
        {
          rest_values = val;
        }
        }
      if (is_subqery)
        {
          PT_NODE *val = pt_sm_attribute_default_value_to_node (parser, db_attr);
          if (!val)
        {
          /* error was already handled */
          goto error;
        }

          if (rest_values)
        {
          rest_values = parser_append_node (val, rest_values);
        }
          else
        {
          rest_values = val;
        }
        }
    }
    }

  if (!rest_attrs || !rest_values)
    {
      /* nothing to do */
      return statement;
    }

  statement->info.insert.attr_list = parser_append_node (rest_attrs, attr_list);
  if (is_values)
    {
      for (crt_node = value_clauses; crt_node; crt_node = crt_node->next)
    {
      /* a different copy of rest_values is needed for each node in the node list */
      PT_NODE *new_rest_values = parser_copy_tree_list (parser, rest_values);
      if (!new_rest_values)
        {
          goto error;
        }
      crt_node->info.node_list.list = parser_append_node (new_rest_values, crt_node->info.node_list.list);
    }
      /* only copied of rest_values are used, has to be freed */
      parser_free_tree (parser, rest_values);
    }
  if (is_subqery)
    {
      statement->info.insert.value_clauses->info.node_list.list =
    pt_append_query_select_list (parser, value_list, rest_values);
    }

  return statement;

error:
  if (rest_attrs)
    {
      parser_free_tree (parser, rest_attrs);
    }
  if (rest_values)
    {
      parser_free_tree (parser, rest_values);
    }
  if (!pt_has_error (parser))
    {
      PT_ERRORm (parser, statement, MSGCAT_SET_ERROR, -(ER_GENERIC_ERROR));
    }

  return NULL;
}

/*
 * pt_resolve_hint_args () -
 *   return: NO_ERROR on success, non-zero for ERROR
 *   parser(in):
 *   arg_list(in/out):
 *   spec_list(in):
 *   discard_no_match(in): remove unmatched node from arg_list
 */
static int
pt_resolve_hint_args (PARSER_CONTEXT * parser, PT_NODE ** arg_list, PT_NODE * spec_list, bool discard_no_match)
{

  PT_NODE *arg, *spec, *range, *prev, *tmp;

  prev = NULL;
  arg = *arg_list;

  while (arg != NULL)
    {
      if (arg->node_type != PT_NAME || arg->info.name.original == NULL)
    {
      goto exit_on_error;
    }

      /* check if the specified class name exists in spec list */
      for (spec = spec_list; spec; spec = spec->next)
    {
      if (spec->node_type != PT_SPEC)
        {
          PT_INTERNAL_ERROR (parser, "resolution");
          goto exit_on_error;
        }

      if ((range = spec->info.spec.range_var)
          && (pt_user_specified_name_compare (range->info.name.original, arg->info.name.original) == 0))
        {
          /* found match */
          arg->info.name.spec_id = spec->info.spec.id;
          arg->info.name.meta_class = PT_HINT_NAME;
          break;
        }
    }

      /* not found */
      if (spec == NULL)
    {
      if (discard_no_match)
        {
          tmp = arg;
          arg = arg->next;
          tmp->next = NULL;
          parser_free_node (parser, tmp);

          if (prev == NULL)
        {
          *arg_list = arg;
        }
          else
        {
          prev->next = arg;
        }
        }
      else
        {
          goto exit_on_error;
        }
    }
      else
    {
      prev = arg;
      arg = arg->next;
    }
    }

  return NO_ERROR;
exit_on_error:

  return ER_FAILED;
}

/*
 * pt_resolve_hint () -
 *   return:
 *   parser(in):
 *   node(in):
 */
static int
pt_resolve_hint (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_HINT_ENUM hint;
  PT_NODE **leading = NULL, **use_nl = NULL, **use_idx = NULL;
  PT_NODE **use_merge = NULL, **index_ss = NULL, **index_ls = NULL;
  PT_NODE **no_use_hash = NULL, **use_hash = NULL;
  PT_NODE *spec_list = NULL;

  switch (node->node_type)
    {
    case PT_SELECT:
      hint = node->info.query.q.select.hint;
      leading = &node->info.query.q.select.leading;
      use_nl = &node->info.query.q.select.use_nl;
      use_idx = &node->info.query.q.select.use_idx;
      index_ss = &node->info.query.q.select.index_ss;
      index_ls = &node->info.query.q.select.index_ls;
      use_merge = &node->info.query.q.select.use_merge;
      no_use_hash = &node->info.query.q.select.no_use_hash;
      use_hash = &node->info.query.q.select.use_hash;
      spec_list = node->info.query.q.select.from;
      break;
    case PT_DELETE:
      hint = node->info.delete_.hint;
      leading = &node->info.delete_.leading_hint;
      use_nl = &node->info.delete_.use_nl_hint;
      use_idx = &node->info.delete_.use_idx_hint;
      use_merge = &node->info.delete_.use_merge_hint;
      no_use_hash = &node->info.delete_.no_use_hash_hint;
      use_hash = &node->info.delete_.use_hash_hint;
      spec_list = node->info.delete_.spec;
      break;
    case PT_UPDATE:
      hint = node->info.update.hint;
      leading = &node->info.update.leading_hint;
      use_nl = &node->info.update.use_nl_hint;
      use_idx = &node->info.update.use_idx_hint;
      use_merge = &node->info.update.use_merge_hint;
      no_use_hash = &node->info.update.no_use_hash_hint;
      use_hash = &node->info.update.use_hash_hint;
      spec_list = node->info.update.spec;
      break;
    default:
      PT_INTERNAL_ERROR (parser, "Invalid statement in hints resolving");
      return ER_FAILED;
    }

  if (hint == PT_HINT_NONE)
    {
      return NO_ERROR;
    }

  if (hint & PT_HINT_LEADING)
    {
      if (pt_resolve_hint_args (parser, leading, spec_list, REQUIRE_ALL_MATCH) != NO_ERROR)
    {
      goto exit_on_error;
    }
    }

  if (hint & PT_HINT_USE_NL)
    {
      if (pt_resolve_hint_args (parser, use_nl, spec_list, REQUIRE_ALL_MATCH) != NO_ERROR)
    {
      goto exit_on_error;
    }
    }

  if (hint & PT_HINT_USE_IDX)
    {
      if (pt_resolve_hint_args (parser, use_idx, spec_list, REQUIRE_ALL_MATCH) != NO_ERROR)
    {
      goto exit_on_error;
    }
    }

  /* *index_ss == NULL means apply index skip scan to each table */
  if ((hint & PT_HINT_INDEX_SS) && *index_ss != NULL)
    {
      if (pt_resolve_hint_args (parser, index_ss, spec_list, DISCARD_NO_MATCH) != NO_ERROR)
    {
      goto exit_on_error;
    }

      /* clear hint if no matched any item */
      if (*index_ss == NULL)
    {
      node->info.query.q.select.hint = (PT_HINT_ENUM) (node->info.query.q.select.hint & ~PT_HINT_INDEX_SS);
    }
    }

  /* *index_ls == NULL means apply loose index scan to each table */
  if ((hint & PT_HINT_INDEX_LS) && *index_ls != NULL)
    {
      if (pt_resolve_hint_args (parser, index_ls, spec_list, DISCARD_NO_MATCH) != NO_ERROR)
    {
      goto exit_on_error;
    }

      /* clear hint if no matched any item */
      if (*index_ls == NULL)
    {
      node->info.query.q.select.hint = (PT_HINT_ENUM) (node->info.query.q.select.hint & ~PT_HINT_INDEX_LS);
    }
    }

  if (hint & PT_HINT_USE_MERGE)
    {
      if (pt_resolve_hint_args (parser, use_merge, spec_list, REQUIRE_ALL_MATCH) != NO_ERROR)
    {
      goto exit_on_error;
    }
    }

  if ((hint & PT_HINT_NO_USE_HASH) && (*no_use_hash != NULL))
    {
      if (pt_resolve_hint_args (parser, no_use_hash, spec_list, DISCARD_NO_MATCH) != NO_ERROR)
    {
      goto exit_on_error;
    }
      if (*no_use_hash == NULL)
    {
      switch (node->node_type)
        {
        case PT_SELECT:
          node->info.query.q.select.hint &= ~PT_HINT_NO_USE_HASH;
          break;
        case PT_DELETE:
          node->info.delete_.hint &= ~PT_HINT_NO_USE_HASH;
          break;
        case PT_UPDATE:
          node->info.update.hint &= ~PT_HINT_NO_USE_HASH;
          break;
        default:
          PT_INTERNAL_ERROR (parser, "Invalid statement in hints resolving");
          goto exit_on_error;
        }
    }
    }

  if ((hint & PT_HINT_USE_HASH) && (*use_hash != NULL))
    {
      if (pt_resolve_hint_args (parser, use_hash, spec_list, DISCARD_NO_MATCH) != NO_ERROR)
    {
      goto exit_on_error;
    }
      if (*use_hash == NULL)
    {
      switch (node->node_type)
        {
        case PT_SELECT:
          node->info.query.q.select.hint &= ~PT_HINT_USE_HASH;
          break;
        case PT_DELETE:
          node->info.delete_.hint &= ~PT_HINT_USE_HASH;
          break;
        case PT_UPDATE:
          node->info.update.hint &= ~PT_HINT_USE_HASH;
          break;
        default:
          PT_INTERNAL_ERROR (parser, "Invalid statement in hints resolving");
          goto exit_on_error;
        }
    }
    }

  return NO_ERROR;
exit_on_error:

  /* clear hint info */
  node->info.query.q.select.hint = PT_HINT_NONE;
  if (*leading != NULL)
    {
      parser_free_tree (parser, *leading);
    }
  if (*use_nl != NULL)
    {
      parser_free_tree (parser, *use_nl);
    }
  if (*use_idx != NULL)
    {
      parser_free_tree (parser, *use_idx);
    }
  if (index_ss != NULL && *index_ss != NULL)
    {
      parser_free_tree (parser, *index_ss);
    }
  if (index_ls != NULL && *index_ls != NULL)
    {
      parser_free_tree (parser, *index_ls);
    }
  if (*use_merge != NULL)
    {
      parser_free_tree (parser, *use_merge);
    }
  if (*no_use_hash != NULL)
    {
      parser_free_tree (parser, *no_use_hash);
    }
  if (*use_hash != NULL)
    {
      parser_free_tree (parser, *use_hash);
    }

  switch (node->node_type)
    {
    case PT_SELECT:
      node->info.query.q.select.leading = NULL;
      node->info.query.q.select.use_nl = NULL;
      node->info.query.q.select.use_idx = NULL;
      node->info.query.q.select.index_ss = NULL;
      node->info.query.q.select.index_ls = NULL;
      node->info.query.q.select.use_merge = NULL;
      node->info.query.q.select.no_use_hash = NULL;
      node->info.query.q.select.use_hash = NULL;
      break;
    case PT_DELETE:
      node->info.delete_.leading_hint = NULL;
      node->info.delete_.use_nl_hint = NULL;
      node->info.delete_.use_idx_hint = NULL;
      node->info.delete_.use_merge_hint = NULL;
      node->info.delete_.no_use_hash_hint = NULL;
      node->info.delete_.use_hash_hint = NULL;
      break;
    case PT_UPDATE:
      node->info.update.leading_hint = NULL;
      node->info.update.use_nl_hint = NULL;
      node->info.update.use_idx_hint = NULL;
      node->info.update.use_merge_hint = NULL;
      node->info.update.no_use_hash_hint = NULL;
      node->info.update.use_hash_hint = NULL;
      break;
    default:
      break;
    }

  return ER_FAILED;
}


static void
pt_write_semantic_warning (PARSER_CONTEXT * parser, PT_NODE * name, int line_no, int msg_no)
{
  char *buf = NULL;
  char *fmt = msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, msg_no);

  if (name->info.name.meta_class != PT_INDEX_NAME)
    {
      asprintf (&buf, fmt, pt_print_bytes (parser, name)->bytes);
    }
  else
    {
      void *ptr = name->etc;
      name->etc = (void *) PT_IDX_HINT_NONE;    // for remove (+)/(-)
      asprintf (&buf, fmt, pt_print_bytes (parser, name)->bytes);
      name->etc = (void *) ptr;
    }

  if (buf)
    {
      er_set (ER_WARNING_SEVERITY, __FILE__, line_no, ER_PT_SEMANTIC, 2, buf, "");
      free (buf);
    }
}

/*
 * pt_resolve_using_index () -
 *   return:
 *   parser(in):
 *   index(in):
 *   from(in):
 */
PT_NODE *
pt_resolve_using_index (PARSER_CONTEXT * parser, PT_NODE * index, PT_NODE * from, bool * is_ignore)
{
  PT_NODE *spec, *range, *entity;
  DB_OBJECT *classop;
  SM_CLASS *class_;
  SM_CLASS_CONSTRAINT *cons;
  int found = 0;
  int errid;
  int err_msg_no, err_line_no;

  assert (index != NULL);

  *is_ignore = false;
  if (index->info.name.original == NULL)
    {
      if (index->etc != (void *) PT_IDX_HINT_CLASS_NONE)
    {
      /* the case of USING INDEX NONE */
      return index;
    }

      // in case "identifier.NONE"
      assert (index->info.name.resolved != NULL);
      assert (index->etc == (void *) PT_IDX_HINT_CLASS_NONE);
    }

  if (index->info.name.spec_id != 0)    /* already resolved */
    {
      return index;
    }

  if (index->info.name.resolved != NULL)
    {
      /* index name is specified by class name as "class.index" */

      /* check if the specified class name exists in spec list */
      for (spec = from; spec; spec = spec->next)
    {
      if (spec->node_type != PT_SPEC)
        {
          PT_INTERNAL_ERROR (parser, "resolution");
          return NULL;
        }

      range = spec->info.spec.range_var;
      entity = spec->info.spec.entity_name;
      if (range && entity
          && (pt_user_specified_name_compare (range->info.name.original, index->info.name.resolved) == 0))
        {
          classop = db_find_class (entity->info.name.original);
          if (au_fetch_class (classop, &class_, AU_FETCH_READ, AU_SELECT) != NO_ERROR)
        {
          assert (er_errid () != NO_ERROR);
          errid = er_errid ();
          if (errid == ER_AU_SELECT_FAILURE || errid == ER_AU_AUTHORIZATION_FAILURE)
            {
              PT_ERRORc (parser, entity, er_msg ());
            }
          else
            {
              PT_INTERNAL_ERROR (parser, "resolution");
            }

          return NULL;
        }
          if (index->info.name.original != NULL)
        {
          cons = classobj_find_class_index (class_, index->info.name.original);
          if (cons == NULL || (cons->index_status != SM_NORMAL_INDEX))
            {
              /* error; the index is not for the specified class or unusable index */
              err_line_no = __LINE__;
              err_msg_no = MSGCAT_SEMANTIC_USING_INDEX_ERR_1;
              goto null_return;
            }
        }

          index->info.name.spec_id = spec->info.spec.id;
          index->info.name.meta_class = PT_INDEX_NAME;
          /* "class.index" is valid */
          return index;
        }
    }

      /* the specified class in "class.index" does not exist in spec list */
      err_line_no = __LINE__;
      err_msg_no = MSGCAT_SEMANTIC_USING_INDEX_ERR_2;
      goto null_return;
    }

  /* case (index->info.name.resolved == NULL) */
  /* index name without class name specification */

  /* find the class of the index from spec list */
  for (spec = from; spec; spec = spec->next)
    {
      if (spec->node_type != PT_SPEC)
    {
      PT_INTERNAL_ERROR (parser, "resolution");
      return NULL;
    }

      range = spec->info.spec.range_var;
      entity = spec->info.spec.entity_name;
      if (range != NULL && entity != NULL && entity->info.name.original != NULL)
    {
      classop = db_find_class (entity->info.name.original);
      if (classop == NULL)
        {
          continue;
        }
      if (au_fetch_class (classop, &class_, AU_FETCH_READ, AU_SELECT) != NO_ERROR)
        {
          assert (er_errid () != NO_ERROR);
          errid = er_errid ();
          if (errid == ER_AU_SELECT_FAILURE || errid == ER_AU_AUTHORIZATION_FAILURE)
        {
          PT_ERRORc (parser, entity, er_msg ());
        }
          else
        {
          PT_INTERNAL_ERROR (parser, "resolution");
        }

          return NULL;
        }
      cons = classobj_find_class_index (class_, index->info.name.original);
      if (cons != NULL && (cons->index_status == SM_NORMAL_INDEX))
        {
          /* found the class; resolve index name */
          found++;
          index->info.name.resolved = range->info.name.original;
          index->info.name.spec_id = spec->info.spec.id;
          index->info.name.meta_class = PT_INDEX_NAME;
        }
      // TODO: raise an error for such indexes??
    }
    }

  if (found == 1)
    {
      return index;
    }

  if (found == 0)
    {
      /* error; can not find the class of the index */
      err_line_no = __LINE__;
      err_msg_no = MSGCAT_SEMANTIC_USING_INDEX_ERR_1;
    }
  else              /* if(found > 1) */
    {
      index->info.name.resolved = NULL;
      /* we found more than one classes which have index of the same name */
      err_line_no = __LINE__;
      err_msg_no = MSGCAT_SEMANTIC_USING_INDEX_ERR_3;
    }

null_return:
  if (PT_SPEC_SPECIAL_INDEX_SCAN (from))
    {
      PT_ERRORmf (parser, index, MSGCAT_SET_PARSER_SEMANTIC, err_msg_no, pt_short_print (parser, index));
    }
  else
    {
      *is_ignore = true;
      pt_write_semantic_warning (parser, index, err_line_no, err_msg_no);
    }
  return NULL;
}

/*
 * pt_str_compare () -
 *   return: 0 if two strings are equal. 1 if not equal
 *   p(in): A string
 *   q(in): A string
 *
 * Note :
 * two NULL strings are considered a match.
 * two strings of length zero match
 * A NULL string does NOT match a zero length string
 */
int
pt_str_compare (const char *p, const char *q, CASE_SENSITIVENESS case_flag)
{
  if (!p && !q)
    {
      return 0;
    }
  if (!p || !q)
    {
      return 1;
    }

  if (case_flag == CASE_INSENSITIVE)
    {
      return intl_identifier_casecmp (p, q);
    }
  else
    {
      return intl_identifier_cmp (p, q);
    }
}

int
pt_user_specified_name_compare (const char *p, const char *q)
{
  const char *dot_p = NULL;
  const char *dot_q = NULL;
  const char *original_p = NULL;
  const char *original_q = NULL;

  if (!p && !q)
    {
      return 0;
    }

  if (!p || !q)
    {
      return 1;
    }

  if (p[0] == '.' || q[0] == '.')
    {
      return 1;
    }

  dot_p = strchr (p, '.');
  dot_q = strchr (q, '.');

  if ((dot_p == NULL && dot_q != NULL) || (dot_p != NULL && dot_q == NULL))
    {
      /*
       * In the case below, only after dot(.) is compared.
       *
       * e.g. p : user_name.object_name -> object_name
       *      q : object_name           -> object_name
       *
       *      or
       *
       *      p : object_name           -> object_name
       *      q : user_name.object_name -> object_name
       */
      original_p = dot_p ? (dot_p + 1) : p;
      original_q = dot_q ? (dot_q + 1) : q;

      /*
       * e.g. original_p : object_name.          -> NULL
       *      original_q : user_name.object_name -> object_name
       *
       *      or
       *
       *      original_p : user_name.object_name -> object_name
       *      original_q : object_name.          -> NULL
       */
      if (original_p[0] == '\0' || original_q[0] == '\0')
    {
      return 1;
    }
    }
  else
    {
      /*
       * In the case below, compare all.
       *
       * e.g. p : user_name.object_name
       *      q : user_name.object_name
       *
       *      or
       *
       *      p : object_name
       *      q : object_name
       */
      original_p = p;
      original_q = q;
    }

  return intl_identifier_casecmp (original_p, original_q);
}

/*
 * pt_get_unique_exposed_name () -
 *   return:
 *
 *   parser(in):
 *   first_spec(in):
 */
static const char *
pt_get_unique_exposed_name (PARSER_CONTEXT * parser, PT_NODE * first_spec)
{
  char name_buf[32];
  int i = 1;

  if (first_spec->node_type != PT_SPEC)
    {
      assert (first_spec->node_type == PT_SPEC);
      return NULL;
    }

  while (1)
    {
      snprintf (name_buf, 32, "__t%u", i);
      if (pt_name_occurs_in_from_list (parser, name_buf, first_spec) == 0)
    {
      return pt_append_string (parser, NULL, name_buf);
    }
      i++;
    }

  return NULL;
}

/*
 * pt_quick_resolve_names () - resolve names in node_p based on the spec
 *                 spec_p
 * return : error code or NO_ERROR
 * parser (in) : parser context
 * spec_p (in/out) : PT_SPEC for the table containing the names in node_p
 * node_p (in/out) : the node to resolve
 * sc_info (in): semantic check info
 *
 * Note: Call this function to resolve the names in a node outside of a
 * statement context
 */
int
pt_quick_resolve_names (PARSER_CONTEXT * parser, PT_NODE ** spec_p, PT_NODE ** node_p, SEMANTIC_CHK_INFO * sc_info)
{
  PT_BIND_NAMES_ARG bind_arg;
  PT_NODE *spec = NULL, *node = NULL;
  int walk = 0;
  SCOPES scopestack;
  PT_EXTRA_SPECS_FRAME spec_frame;
  PT_FLAT_SPEC_INFO info;

  if (node_p == NULL || spec_p == NULL)
    {
      assert (false);
      return ER_FAILED;
    }

  spec = *spec_p;
  node = *node_p;
  if (spec == NULL || node == NULL)
    {
      assert (false);
      return ER_FAILED;
    }

  /* convert spec to a flat entity list */
  info.spec_parent = NULL;
  info.for_update = false;
  spec = pt_flat_spec_pre (parser, spec, &info, &walk);

  /* bind spec in scope */
  bind_arg.scopes = NULL;
  bind_arg.spec_frames = NULL;
  bind_arg.sc_info = sc_info;

  scopestack.next = bind_arg.scopes;
  scopestack.specs = spec;
  scopestack.correlation_level = 0;
  scopestack.location = 0;

  bind_arg.scopes = &scopestack;
  spec_frame.next = bind_arg.spec_frames;
  spec_frame.extra_specs = NULL;
  bind_arg.spec_frames = &spec_frame;

  *spec_p = spec;
  node = *node_p;
  /* resolve expression */
  node = parser_walk_tree (parser, node, pt_bind_names, &bind_arg, pt_bind_names_post, &bind_arg);

  node_p = &node;
  return NO_ERROR;
}

/*
 * natural_join_equal_attr () - If the two attributes have same name,
 *     the function return true. We don't consider the type there.
 *     Whether the join can be executed is dependent on whether the
 *     two types are compatible. If not, the error will be threw by
 *     subsequent process.
 *   return:
 *   lhs(in):
 *   rhs(in):
 */
static bool
natural_join_equal_attr (NATURAL_JOIN_ATTR_INFO * lhs, NATURAL_JOIN_ATTR_INFO * rhs)
{
  const char *lhs_name;
  const char *rhs_name;

  assert (lhs != NULL && rhs != NULL);

  lhs_name = lhs->name;
  rhs_name = rhs->name;

  if (lhs_name == NULL || rhs_name == NULL)
    {
      return false;
    }

  if (intl_identifier_casecmp (lhs_name, rhs_name) == 0)
    {
      return true;
    }

  return false;
}

/*
 * free_natural_join_attrs () -
 *   return:
 *   attrs(in):
 */
static void
free_natural_join_attrs (NATURAL_JOIN_ATTR_INFO * attrs)
{
  NATURAL_JOIN_ATTR_INFO *attr_cur;
  NATURAL_JOIN_ATTR_INFO *attr_cur_next;

  attr_cur = attrs;
  while (attr_cur != NULL)
    {
      attr_cur_next = attr_cur->next;
      free (attr_cur);
      attr_cur = attr_cur_next;
    }
}

/*
 * generate_natural_join_attrs_from_subquery () -
 *   return:
 *   subquery_attrs_list(in):
 *   attrs_p(out):
 */
static int
generate_natural_join_attrs_from_subquery (PT_NODE * subquery_attrs_list, NATURAL_JOIN_ATTR_INFO ** attrs_p)
{
  PT_NODE *pt_cur;
  NATURAL_JOIN_ATTR_INFO *attr_head = NULL;
  NATURAL_JOIN_ATTR_INFO *attr_tail = NULL;
  NATURAL_JOIN_ATTR_INFO *attr_cur;

  for (pt_cur = subquery_attrs_list; pt_cur != NULL; pt_cur = pt_cur->next)
    {
      /*
       * We just deal the attributes which have name. It means we just
       * deal PT_NAME or other's pt_node have alias_name. For example,
       * select 1 from t1. The '1' is impossible to be used in natural
       * join, so we skip it.
       */

      if (pt_cur->alias_print == NULL && pt_cur->node_type != PT_NAME)
    {
      continue;
    }

      attr_cur = (NATURAL_JOIN_ATTR_INFO *) malloc (sizeof (NATURAL_JOIN_ATTR_INFO));
      if (attr_cur == NULL)
    {
      goto exit_on_error;
    }

      attr_cur->next = NULL;

      /*
       * Alias name have higher priority. select a as txx from ....
       * We consider txx as the attribute's name and ignore a.
       */
      if (pt_cur->alias_print)
    {
      attr_cur->name = (char *) pt_cur->alias_print;
    }
      else
    {
      if (pt_cur->node_type == PT_NAME)
        {
          attr_cur->name = (char *) pt_cur->info.name.original;
        }
    }

      attr_cur->type_enum = pt_cur->type_enum;

      attr_cur->meta_class = PT_NORMAL;
      if (pt_cur->node_type == PT_NAME)
    {
      attr_cur->meta_class = pt_cur->info.name.meta_class;
    }

      if (attr_head == NULL)
    {
      attr_head = attr_cur;
      attr_tail = attr_cur;
    }
      else
    {
      attr_tail->next = attr_cur;
      attr_tail = attr_cur;
    }
    }

  *attrs_p = attr_head;
  return NO_ERROR;

exit_on_error:
  free_natural_join_attrs (attr_head);
  return ER_OUT_OF_VIRTUAL_MEMORY;
}


/*
 * generate_natural_join_attrs_from_db_attrs () -
 *   return:
 *   db_attrs(in):
 *   attrs_p(out):
 */
static int
generate_natural_join_attrs_from_db_attrs (DB_ATTRIBUTE * db_attrs, NATURAL_JOIN_ATTR_INFO ** attrs_p)
{
  DB_ATTRIBUTE *db_attr_cur;
  NATURAL_JOIN_ATTR_INFO *attr_head = NULL;
  NATURAL_JOIN_ATTR_INFO *attr_tail = NULL;
  NATURAL_JOIN_ATTR_INFO *attr_cur;

  for (db_attr_cur = db_attrs; db_attr_cur != NULL; db_attr_cur = db_attribute_next (db_attr_cur))
    {
      attr_cur = (NATURAL_JOIN_ATTR_INFO *) malloc (sizeof (NATURAL_JOIN_ATTR_INFO));
      if (attr_cur == NULL)
    {
      goto exit_on_error;
    }

      attr_cur->next = NULL;
      attr_cur->name = (char *) db_attribute_name (db_attr_cur);
      attr_cur->type_enum = pt_db_to_type_enum (db_attribute_type (db_attr_cur));
      attr_cur->meta_class = (db_attribute_is_shared (db_attr_cur) ? PT_SHARED : PT_NORMAL);

      if (attr_head == NULL)
    {
      attr_head = attr_cur;
      attr_tail = attr_cur;
    }
      else
    {
      attr_tail->next = attr_cur;
      attr_tail = attr_cur;
    }
    }

  *attrs_p = attr_head;
  return NO_ERROR;

exit_on_error:
  free_natural_join_attrs (attr_head);
  return ER_OUT_OF_VIRTUAL_MEMORY;
}

/*
 * get_natural_join_attrs_from_pt_spec () - Get all attributes from a pt_spec
 *     node that indicates an table or a subquery.
 *   return:
 *   parser(in):
 *   node(in):
 */
static NATURAL_JOIN_ATTR_INFO *
get_natural_join_attrs_from_pt_spec (PARSER_CONTEXT * parser, PT_NODE * node)
{
  DB_OBJECT *cls;
  DB_ATTRIBUTE *db_attrs;
  NATURAL_JOIN_ATTR_INFO *natural_join_attrs;
  PT_NODE *derived_table;
  PT_NODE *subquery_attrs_list;

  assert (node != NULL && node->node_type == PT_SPEC);

  cls = NULL;
  db_attrs = NULL;
  natural_join_attrs = NULL;

  if (PT_SPEC_IS_ENTITY (node))
    {
      /* This is a table. */
      cls = node->info.spec.entity_name->info.name.db_object;
      if (cls == NULL)
    {
      return NULL;
    }

      db_attrs = db_get_attributes (cls);
      if (db_attrs == NULL)
    {
      return NULL;
    }

      if (generate_natural_join_attrs_from_db_attrs (db_attrs, &natural_join_attrs) != NO_ERROR)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      goto exit_on_error;
    }
    }
  else if (PT_SPEC_IS_DERIVED (node) || PT_SPEC_IS_CTE (node))
    {
      /* This is a subquery or a CTE. */
      if (PT_SPEC_IS_DERIVED (node) && node->info.spec.derived_table_type == PT_IS_SUBQUERY)
    {
      derived_table = node->info.spec.derived_table;

      if (node->info.spec.as_attr_list != NULL)
        {
          subquery_attrs_list = node->info.spec.as_attr_list;
        }
      else if (derived_table->node_type == PT_SELECT)
        {
          subquery_attrs_list = derived_table->info.query.q.select.list;
        }
      else
        {
          subquery_attrs_list = NULL;
        }
    }
      else
    {
      subquery_attrs_list = node->info.spec.as_attr_list;
    }

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

      if (generate_natural_join_attrs_from_subquery (subquery_attrs_list, &natural_join_attrs) != NO_ERROR)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      goto exit_on_error;
    }

    }
  else
    {
      assert (false);
    }

  return natural_join_attrs;

exit_on_error:
  return NULL;
}

/*
 * pt_create_pt_expr_equal_node () - The function creates the PT_expr
 *     for natural join. The operator is " = ".
 *   return:
 *   parser(in):
 *   arg1(in):
 *   arg2(in):
 */
static PT_NODE *
pt_create_pt_expr_equal_node (PARSER_CONTEXT * parser, PT_NODE * arg1, PT_NODE * arg2)
{
  PT_NODE *expr = NULL;

  expr = parser_new_node (parser, PT_EXPR);
  if (expr == NULL)
    {
      return NULL;
    }

  expr->type_enum = PT_TYPE_LOGICAL;
  expr->info.expr.op = PT_EQ;
  expr->info.expr.arg1 = arg1;
  expr->info.expr.arg2 = arg2;

  return expr;
}

/*
 * pt_create_pt_name () - The function creates the PT_NAME name for natural
 *     join. The pt_name node indicates an attribute in a table/subquery.
 *     The spec indicates the table/subquery.
 *   return:
 *   parser(in):
 *   spec(in):
 *   attr(in):
 */
static PT_NODE *
pt_create_pt_name (PARSER_CONTEXT * parser, PT_NODE * spec, NATURAL_JOIN_ATTR_INFO * attr)
{
  PT_NODE *name;

  assert (attr != NULL);
  assert (spec != NULL);

  name = parser_new_node (parser, PT_NAME);
  if (name == NULL)
    {
      return NULL;
    }

  name->info.name.original = pt_append_string (parser, NULL, attr->name);
  name->type_enum = attr->type_enum;
  name->info.name.meta_class = attr->meta_class;
  if (PT_SPEC_IS_ENTITY (spec))
    {
      name->info.name.resolved = pt_append_string (parser, NULL, spec->info.spec.entity_name->info.name.original);
    }
  else if (PT_SPEC_IS_CTE (spec))
    {
      PT_NODE *cte = spec->info.spec.cte_pointer->info.pointer.node;
      name->info.name.resolved = pt_append_string (parser, NULL, cte->info.cte.name->info.name.original);
    }
  else
    {
      assert (PT_SPEC_IS_DERIVED (spec));
    }

  if (PT_IS_SPEC_REAL_TABLE (spec))
    {
      PT_NAME_INFO_SET_FLAG (name, PT_NAME_DEFAULTF_ACCEPTS);
    }

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

  return name;

}

/*
 * pt_create_pt_expr_and_node () - The function create the PT_expr for natural
 *     join. The operator is AND.
 *   return:
 *   parser(in):
 *   arg1(in):
 *   arg2(in):
 */
static PT_NODE *
pt_create_pt_expr_and_node (PARSER_CONTEXT * parser, PT_NODE * arg1, PT_NODE * arg2)
{
  PT_NODE *expr = NULL;

  expr = parser_new_node (parser, PT_EXPR);
  if (expr == NULL)
    {
      return NULL;
    }

  expr->type_enum = PT_TYPE_LOGICAL;
  expr->info.expr.op = PT_AND;
  expr->info.expr.arg1 = arg1;
  expr->info.expr.arg2 = arg2;

  return expr;
}

/*
 * pt_resolve_natural_join_internal () - Resolve natural join into inner/outer join actually.
 *     For t1 natural join t2, join_lhs is t1 and join_rhs is t2. The function adds on_cond
 *     into t2. After the process, the join will become an inner/outer join.
 *   return:
 *   parser(in):
 *   join_lhs(in):
 *   join_rhs(in/out):
 */
static void
pt_resolve_natural_join_internal (PARSER_CONTEXT * parser, PT_NODE * join_lhs, PT_NODE * join_rhs)
{
  NATURAL_JOIN_ATTR_INFO *lhs_attrs;
  NATURAL_JOIN_ATTR_INFO *rhs_attrs;
  NATURAL_JOIN_ATTR_INFO *lhs_attrs_cur, *rhs_attrs_cur;
  PT_NODE *on_cond_tail;
  PT_NODE *join_cond_expr;
  PT_NODE *join_cond_arg1;
  PT_NODE *join_cond_arg2;
  PT_NODE *on_cond_new;

  assert (join_lhs != NULL);
  assert (join_rhs != NULL);

  on_cond_tail = join_rhs->info.spec.on_cond;

  lhs_attrs = get_natural_join_attrs_from_pt_spec (parser, join_lhs);
  rhs_attrs = get_natural_join_attrs_from_pt_spec (parser, join_rhs);

  for (lhs_attrs_cur = lhs_attrs; lhs_attrs_cur != NULL; lhs_attrs_cur = lhs_attrs_cur->next)
    {
      for (rhs_attrs_cur = rhs_attrs; rhs_attrs_cur; rhs_attrs_cur = rhs_attrs_cur->next)
    {
      if (!natural_join_equal_attr (lhs_attrs_cur, rhs_attrs_cur))
        {
          continue;
        }

      /* step 1: we create pt_name for the first attribute */
      join_cond_arg1 = pt_create_pt_name (parser, join_lhs, lhs_attrs_cur);
      if (join_cond_arg1 == NULL)
        {
          PT_ERRORm (parser, join_rhs, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          goto exit_on_create_node_error;
        }

      /* step 2: we create pt_name for the second attribute */
      join_cond_arg2 = pt_create_pt_name (parser, join_rhs, rhs_attrs_cur);
      if (join_cond_arg2 == NULL)
        {
          PT_ERRORm (parser, join_rhs, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          goto exit_on_create_node_error;
        }

      /* step 3: we create the equal pt_expr node. like "join_cond_arg1 = join_cond_arg2" */
      join_cond_expr = pt_create_pt_expr_equal_node (parser, join_cond_arg1, join_cond_arg2);
      if (join_cond_expr == NULL)
        {
          PT_ERRORm (parser, join_rhs, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          goto exit_on_create_node_error;
        }

      /*
       * step4: If there is no on_cond, the new expr we created will be on_cond.
       *   If not, it means there is old on_conds. So we will create a new expr
       *   like "(old on_cond) and (new on_cond)".
       */
      if (on_cond_tail == NULL)
        {
          on_cond_tail = join_cond_expr;
        }
      else
        {
          on_cond_new = pt_create_pt_expr_and_node (parser, on_cond_tail, join_cond_expr);
          if (on_cond_new == NULL)
        {
          PT_ERRORm (parser, join_rhs, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
          goto exit_on_create_node_error;
        }

          on_cond_tail = on_cond_new;
        }
    }
    }

  join_rhs->info.spec.on_cond = on_cond_tail;

  if (lhs_attrs != NULL)
    {
      free_natural_join_attrs (lhs_attrs);
    }

  if (rhs_attrs != NULL)
    {
      free_natural_join_attrs (rhs_attrs);
    }

  return;

exit_on_create_node_error:
  if (lhs_attrs != NULL)
    {
      free_natural_join_attrs (lhs_attrs);
    }

  if (rhs_attrs != NULL)
    {
      free_natural_join_attrs (rhs_attrs);
    }

  return;
}

/*
 * pt_resolve_natural_join () - Resolve natural join into inner/outer join.
 *   return:
 *   parser(in):
 *   node(in/out):
 *   chk_parent(in):
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_resolve_natural_join (PARSER_CONTEXT * parser, PT_NODE * node, void *chk_parent, int *continue_walk)
{
  PT_NODE *join_lhs, *join_rhs;

  *continue_walk = PT_CONTINUE_WALK;

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

  join_lhs = node;
  join_rhs = node->next;

  /* there is a natural join */
  if (join_rhs != NULL && join_rhs->node_type == PT_SPEC && join_rhs->info.spec.natural == true)
    {
      pt_resolve_natural_join_internal (parser, join_lhs, join_rhs);
    }

  return node;
}

/*
 * is_pt_name_in_group_having () -
 *   return:
 *   node(in):
 */
static bool
is_pt_name_in_group_having (PT_NODE * node)
{
  if (node == NULL || node->node_type != PT_NAME || node->etc == NULL)
    {
      return false;
    }

  if (intl_identifier_casecmp ((char *) node->etc, CPTR_PT_NAME_IN_GROUP_HAVING) == 0)
    {
      return true;
    }

  return false;
}

/*
 * pt_mark_pt_name () -
 *   return:
 *   parser(in):
 *   node(in/out):
 *   chk_parent(in):
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_mark_pt_name (PARSER_CONTEXT * parser, PT_NODE * node, void *chk_parent, int *continue_walk)
{
  *continue_walk = PT_CONTINUE_WALK;

  if (node == NULL || node->node_type != PT_NAME)
    {
      return node;
    }
  node->etc = (void *) pt_append_string (parser, NULL, CPTR_PT_NAME_IN_GROUP_HAVING);

  return node;
}

/*
 * pt_mark_group_having_pt_name () - Mark the PT_NAME in group by / having.
 *   return:
 *   parser(in):
 *   node(in/out):
 *   chk_parent(in):
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_mark_group_having_pt_name (PARSER_CONTEXT * parser, PT_NODE * node, void *chk_parent, int *continue_walk)
{
  *continue_walk = PT_CONTINUE_WALK;

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

  if (node->info.query.q.select.group_by != NULL)
    {
      node->info.query.q.select.group_by =
    parser_walk_tree (parser, node->info.query.q.select.group_by, pt_mark_pt_name, NULL, NULL, NULL);
    }

  if (node->info.query.q.select.having != NULL)
    {
      node->info.query.q.select.having =
    parser_walk_tree (parser, node->info.query.q.select.having, pt_mark_pt_name, NULL, NULL, NULL);
    }

  if (!PT_SELECT_INFO_IS_FLAGED (node, PT_SELECT_INFO_COLS_SCHEMA | PT_SELECT_FULL_INFO_COLS_SCHEMA))
    {
      if (node->info.query.order_by != NULL)
    {
      node->info.query.order_by =
        parser_walk_tree (parser, node->info.query.order_by, pt_mark_pt_name, NULL, NULL, NULL);
    }
    }

  return node;
}

/*
 * pt_resolve_group_having_alias_pt_sort_spec () -
 *   return:
 *   parser(in):
 *   node(in/out):
 *   select_list(in):
 */
static void
pt_resolve_group_having_alias_pt_sort_spec (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * select_list)
{
  if (node != NULL && node->node_type == PT_SORT_SPEC)
    {
      pt_resolve_group_having_alias_internal (parser, &(node->info.sort_spec.expr), select_list);
    }
}

/*
 * pt_resolve_group_having_alias_pt_name () -
 *   return:
 *   parser(in):
 *   node_p(in/out):
 *   select_list(in):
 */
static void
pt_resolve_group_having_alias_pt_name (PARSER_CONTEXT * parser, PT_NODE ** node_p, PT_NODE * select_list)
{
  PT_NODE *col;
  char *n_str;
  PT_NODE *node;

  assert (node_p != NULL);

  node = *node_p;

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

  /* It have been resolved. */
  if (node->info.name.resolved != NULL)
    {
      return;
    }

  n_str = (char *) node->info.name.original;

  for (col = select_list; col != NULL; col = col->next)
    {
      if (col->alias_print != NULL && intl_identifier_casecmp (n_str, col->alias_print) == 0)
    {
      parser_free_node (parser, *node_p);
      *node_p = parser_copy_tree (parser, col);
      if ((*node_p) != NULL)
        {
          (*node_p)->next = NULL;
        }
      break;
    }
    }

  /* We can not resolve the pt_name. */
  if (col == NULL)
    {
      PT_ERRORmf (parser, (*node_p), MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_DEFINED,
          pt_short_print (parser, (*node_p)));
    }
}

/*
 * pt_resolve_group_having_alias_pt_expr () -
 *   return:
 *   parser(in):
 *   node(in/out):
 *   select_list(in):
 */
static void
pt_resolve_group_having_alias_pt_expr (PARSER_CONTEXT * parser, PT_NODE * node, PT_NODE * select_list)
{
  if (node == NULL || node->node_type != PT_EXPR)
    {
      return;
    }

  /* Resolve arg1 */
  if (node->info.expr.arg1 != NULL && node->info.expr.arg1->node_type == PT_NAME)
    {
      pt_resolve_group_having_alias_pt_name (parser, &node->info.expr.arg1, select_list);
    }
  else if (node->info.expr.arg1 != NULL && node->info.expr.arg1->node_type == PT_EXPR)
    {
      pt_resolve_group_having_alias_pt_expr (parser, node->info.expr.arg1, select_list);
    }
  else
    {

    }

  /* Resolve arg2 */
  if (node->info.expr.arg2 != NULL && node->info.expr.arg2->node_type == PT_NAME)
    {
      pt_resolve_group_having_alias_pt_name (parser, &node->info.expr.arg2, select_list);
    }
  else if (node->info.expr.arg2 != NULL && node->info.expr.arg2->node_type == PT_EXPR)
    {
      pt_resolve_group_having_alias_pt_expr (parser, node->info.expr.arg2, select_list);
    }
  else
    {

    }

  /* Resolve arg3 */
  if (node->info.expr.arg3 != NULL && node->info.expr.arg3->node_type == PT_NAME)
    {
      pt_resolve_group_having_alias_pt_name (parser, &node->info.expr.arg3, select_list);
    }
  else if (node->info.expr.arg3 != NULL && node->info.expr.arg3->node_type == PT_EXPR)
    {
      pt_resolve_group_having_alias_pt_expr (parser, node->info.expr.arg3, select_list);
    }
  else
    {

    }
}

/*
 * pt_resolve_group_having_alias_internal () - Rosolve alias name in groupby and having clause.
 *   return:
 *   parser(in):
 *   node_p(in/out):
 *   select_list(in):
 */
static void
pt_resolve_group_having_alias_internal (PARSER_CONTEXT * parser, PT_NODE ** node_p, PT_NODE * select_list)
{
  assert (node_p != NULL);
  assert ((*node_p) != NULL);

  switch ((*node_p)->node_type)
    {
    case PT_NAME:
      pt_resolve_group_having_alias_pt_name (parser, node_p, select_list);
      break;
    case PT_EXPR:
      pt_resolve_group_having_alias_pt_expr (parser, *node_p, select_list);
      break;
    case PT_SORT_SPEC:
      pt_resolve_group_having_alias_pt_sort_spec (parser, *node_p, select_list);
      break;
    default:
      return;
    }
  return;
}

/*
 * pt_resolve_group_having_alias () - Resolve alias name in groupby and having clause. We
 *     resolve groupby/having alias after bind_name, it means when the alias name is same
 *     with table attribute, we choose table attribute firstly.
 *   return:
 *   parser(in):
 *   node(in/out):
 *   chk_parent(in):
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_resolve_group_having_alias (PARSER_CONTEXT * parser, PT_NODE * node, void *chk_parent, int *continue_walk)
{
  PT_NODE *pt_cur;

  *continue_walk = PT_CONTINUE_WALK;

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

  /* support for alias in GROUP BY */
  pt_cur = node->info.query.q.select.group_by;
  while (pt_cur != NULL)
    {
      pt_resolve_group_having_alias_internal (parser, &pt_cur, node->info.query.q.select.list);
      pt_cur = pt_cur->next;
    }

  /* support for alias in HAVING */
  pt_cur = node->info.query.q.select.having;
  while (pt_cur != NULL)
    {
      pt_resolve_group_having_alias_internal (parser, &pt_cur, node->info.query.q.select.list);
      pt_cur = pt_cur->next;
    }

  /* support for alias in ORDER BY */
  if (!PT_SELECT_INFO_IS_FLAGED (node, PT_SELECT_INFO_COLS_SCHEMA | PT_SELECT_FULL_INFO_COLS_SCHEMA))
    {
      pt_cur = node->info.query.order_by;
      while (pt_cur != NULL)
    {
      pt_resolve_group_having_alias_internal (parser, &pt_cur, node->info.query.q.select.list);
      pt_cur = pt_cur->next;
    }
    }
  return node;
}

/*
 * pt_resolve_names () -
 *   return:
 *   parser(in):
 *   statement(in):
 *   sc_info(in):
 */
PT_NODE *
pt_resolve_names (PARSER_CONTEXT * parser, PT_NODE * statement, SEMANTIC_CHK_INFO * sc_info)
{
  PT_BIND_NAMES_ARG bind_arg;
  PT_FLAT_SPEC_INFO info;

  bind_arg.scopes = NULL;
  bind_arg.spec_frames = NULL;
  bind_arg.sc_info = sc_info;

  assert (sc_info != NULL);

  if (statement != NULL && statement->node_type == PT_MERGE && statement->info.merge.into != NULL)
    {
      /* chain merge specs for flat name resolving */
      statement->info.merge.into->next = statement->info.merge.using_clause;
      statement->info.merge.using_clause = NULL;
    }

  /* Replace each Entity Spec with an Equivalent flat list */
  info.spec_parent = NULL;
  info.for_update = false;
  statement = parser_walk_tree (parser, statement, pt_flat_spec_pre, &info, pt_continue_walk, NULL);

  if (statement != NULL && statement->node_type == PT_MERGE && statement->info.merge.into != NULL)
    {
      /* unchain merge specs */
      statement->info.merge.using_clause = statement->info.merge.into->next;
      statement->info.merge.into->next = NULL;
    }

  /* resolve names in search conditions, assignments, and assignations */
  if (!pt_has_error (parser))
    {
      PT_NODE *idx_name = NULL;
      if (statement->node_type == PT_CREATE_INDEX || statement->node_type == PT_ALTER_INDEX
      || statement->node_type == PT_DROP_INDEX)
    {
      /* backup the name of the index because it is not part of the table spec yet */
      idx_name = statement->info.index.index_name;
      statement->info.index.index_name = NULL;
    }

      /* Before pt_bind_name, we mark PT_NAME in group by/ having. */
      statement = parser_walk_tree (parser, statement, pt_mark_group_having_pt_name, NULL, NULL, NULL);
      if (pt_has_error (parser))
    {
      return NULL;
    }

      statement = parser_walk_tree (parser, statement, pt_bind_names, &bind_arg, pt_bind_names_post, &bind_arg);
      if (pt_has_error (parser))
    {
      return NULL;
    }

      if (statement
      && (statement->node_type == PT_CREATE_INDEX || statement->node_type == PT_ALTER_INDEX
          || statement->node_type == PT_DROP_INDEX))
    {
      statement->info.index.index_name = idx_name;
    }

      /* Resolve alias in group by/having. */
      statement = parser_walk_tree (parser, statement, pt_resolve_group_having_alias, NULL, NULL, NULL);
      if (pt_has_error (parser))
    {
      return NULL;
    }

      /*
       * The process converts natural join to inner/outer join.
       * The on_cond is added there.
       */
      statement = parser_walk_tree (parser, statement, NULL, NULL, pt_resolve_natural_join, NULL);
      if (pt_has_error (parser))
    {
      return NULL;
    }
    }

  /* Flag specs from FOR UPDATE clause with PT_SPEC_FLAG_FOR_UPDATE_CLAUSE and clear the for_update list. From now on
   * the specs from FOR UPDATE clause can be determined using this flag together with PT_SELECT_INFO_FOR_UPDATE flag */
  if (statement != NULL && statement->node_type == PT_SELECT
      && PT_SELECT_INFO_IS_FLAGED (statement, PT_SELECT_INFO_FOR_UPDATE))
    {
      PT_NODE *node = statement->info.query.q.select.for_update;
      PT_NODE *spec = NULL;
      PT_NODE *entity;

      /* to process the clause "FOR UPDATE OF" */
      if (statement->info.query.q.select.for_update != NULL)
    {
      /* Flag only the specified specs */
      for (; node != NULL; node = node->next)
        {
          spec = pt_find_spec (parser, statement->info.query.q.select.from, node);
          if (spec == NULL)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CLASS_DOES_NOT_EXIST,
                  node->info.name.original);
          return NULL;
        }

          for (entity = spec->info.spec.flat_entity_list; entity; entity = entity->next)
        {
          if (sm_check_system_class_by_name (entity->info.name.original))
            {
              PT_ERRORmf2 (parser, entity, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_IS_NOT_AUTHORIZED_ON,
                   "UPDATE", entity->info.name.original);
              return NULL;
            }
        }
          spec->info.spec.flag = (PT_SPEC_FLAG) (spec->info.spec.flag | PT_SPEC_FLAG_FOR_UPDATE_CLAUSE);
        }
      parser_free_tree (parser, statement->info.query.q.select.for_update);
      statement->info.query.q.select.for_update = NULL;
    }
      else
    {
      /* Flag all specs */
      for (spec = statement->info.query.q.select.from; spec != NULL; spec = spec->next)
        {
          for (entity = spec->info.spec.flat_entity_list; entity; entity = entity->next)
        {
          if (sm_check_system_class_by_name (entity->info.name.original))
            {
              PT_ERRORmf2 (parser, entity, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_IS_NOT_AUTHORIZED_ON,
                   "UPDATE", entity->info.name.original);
              return NULL;
            }
        }
          spec->info.spec.flag = (PT_SPEC_FLAG) (spec->info.spec.flag | PT_SPEC_FLAG_FOR_UPDATE_CLAUSE);
        }
    }
    }

  return statement;
}

/* pt_resolve_spec_to_cte - search for matches of node spec in each CTE from cte_defs list
 *
 *   return:
 *   parser(in):
 *   node(in/out):
 *   arg(in): cte_defs ( only the CTEs that can be referenced )
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_resolve_spec_to_cte (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE *cte_defs = (PT_NODE *) arg;
  PT_NODE *cte = NULL;

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

  if (node->node_type != PT_SPEC || !PT_SPEC_IS_ENTITY (node))
    {
      /* not interested in non-entity-specs */
      return node;
    }

  for (cte = cte_defs; cte != NULL; cte = cte->next)
    {
      PT_NODE *cte_name = cte->info.cte.name;
      assert (cte_name != NULL);

      if (pt_name_equal (parser, cte_name, node->info.spec.entity_name)
      || pt_user_specified_name_compare (cte_name->info.name.original,
                         node->info.spec.entity_name->info.name.original) == 0)
    {
      node->info.spec.cte_name = node->info.spec.entity_name;
      node->info.spec.entity_name = NULL;
      node->info.spec.cte_pointer = pt_point (parser, cte);
      node->info.spec.cte_pointer->info.pointer.do_walk = false;
      break;
    }
    }

  return node;
}

/* pt_resolve_spec_to_cte_and_count - search for matches of spec in each CTE from cte_defs list
 *                                    and sets node->etc as counter if found
 *
 *   return:
 *   parser(in):
 *   node(in/out):
 *   arg(in): cte_defs ( only the CTEs that can be referenced )
 *   continue_walk(in/out):
 *
 *   Note: Must be paired with pt_count_ctes_post to ensure node->etc cleanup
 */
static PT_NODE *
pt_resolve_spec_to_cte_and_count (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  if (node == NULL)
    {
      assert (false);
      return NULL;
    }

  /* we are interested only in entity-specs that are transformed into cte-specs after pt_resolve_spec_to_cte */
  if (node->node_type != PT_SPEC || !PT_SPEC_IS_ENTITY (node))
    {
      return node;
    }

  node = pt_resolve_spec_to_cte (parser, node, arg, continue_walk);
  if (node == NULL || !PT_SPEC_IS_CTE (node))
    {
      /* spec was not found | error occured */
      return node;
    }

  /* spec was transformed into a CTE pointer spec */
  assert (node->node_type == PT_SPEC && PT_SPEC_IS_CTE (node));

  /* set node->etc to the number of occurences (one) */
  node->etc = malloc (sizeof (char));
  *((char *) node->etc) = 1;

  return node;
}

/* pt_count_ctes_post () - increase counter with current node cte occurrences
*   return:
*   parser(in):
*   node(in): the node to check, leave node unchanged except of node->etc
*   arg(out): count of CTEs
*   continue_walk(in):
*/
static PT_NODE *
pt_count_ctes_post (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  int *cnt = (int *) arg;

  /* check if node is a CTE SPEC with node->etc set */
  if (node != NULL && node->node_type == PT_SPEC && PT_SPEC_IS_CTE (node) && pt_node_etc (node) != NULL)
    {
      /* increment cnt argument and free etc */
      assert (*((char *) pt_node_etc (node)) == 1);
      (*cnt)++;

      /* cte_matches counter is no longer needed; pt_resolve_spec_to_cte expects to clean it here */
      free (pt_node_etc (node));
      pt_null_etc (node);
    }

  return node;
}

/* pt_resolve_cte_specs () - resolves all CTEs involved in a query
*   return:
*   parser(in):
*   node(in):
*   arg(out):
*   continue_walk(in):
*/
PT_NODE *
pt_resolve_cte_specs (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE *cte_list, *with = NULL, *saved_with = NULL;
  PT_NODE **with_p;
  PT_NODE *curr_cte, *previous_cte;
  PT_NODE *saved_curr_cte_next;
  int nested_with_count = 0;

  assert (parser != NULL);
  assert (node != NULL);

  switch (node->node_type)
    {
    case PT_SELECT:
    case PT_UNION:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
      with = node->info.query.with;
      with_p = &node->info.query.with;
      break;
    case PT_UPDATE:
      with = node->info.update.with;
      with_p = &node->info.update.with;
      break;
    case PT_DELETE:
      with = node->info.delete_.with;
      with_p = &node->info.delete_.with;
      break;

    default:
      return node;
    }

  if (with == NULL)
    {
      /* nothing to resolve to */
      return node;
    }

  cte_list = with->info.with_clause.cte_definition_list;
  if (cte_list == NULL)
    {
      PT_INTERNAL_ERROR (parser, "expecting cte definitions");
      return NULL;
    }

  /* check ambiguity in the names of CTEs */
  for (previous_cte = cte_list; previous_cte != NULL; previous_cte = previous_cte->next)
    {
      for (curr_cte = previous_cte->next; curr_cte != NULL; curr_cte = curr_cte->next)
    {
      if (pt_name_equal (parser, previous_cte->info.cte.name, curr_cte->info.cte.name))
        {
          /* ambiguity, two CTEs with the same name */
          PT_ERRORmf (parser, with, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CTE_NAME_AMBIGUITY,
              curr_cte->info.cte.name->info.name.original);
          return NULL;
        }
    }
    }

  /* check if there are nested WITHs */
  cte_list = parser_walk_tree (parser, cte_list, pt_count_with_clauses, &nested_with_count, NULL, NULL);
  if (nested_with_count > 0)
    {
      PT_ERRORm (parser, with, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NESTED_WITH);
      return NULL;
    }

  /* STEP 1: Check if CTE is recursive - find if CTE definition contains self references and resolve them */
  for (curr_cte = cte_list; curr_cte != NULL; curr_cte = curr_cte->next)
    {
      if ((curr_cte->info.cte.non_recursive_part->node_type == PT_UNION) && (!curr_cte->info.cte.recursive_part))
    {
      PT_NODE *recursive_part = curr_cte->info.cte.non_recursive_part->info.query.q.union_.arg2;
      PT_NODE *non_recursive_part = curr_cte->info.cte.non_recursive_part->info.query.q.union_.arg1;
      PT_MISC_TYPE all_distinct = curr_cte->info.cte.non_recursive_part->info.query.all_distinct;
      PT_NODE *from;
      int curr_cte_count = 0;

      if (recursive_part->node_type != PT_SELECT)
        {
          /* SELECT only */
          continue;
        }

      /* isolate curr_cte to prevent resolving next CTEs in current recursive part */
      saved_curr_cte_next = curr_cte->next;
      curr_cte->next = NULL;

      /* look for self references in recursive query FROM */
      for (from = recursive_part->info.query.q.select.from; from != NULL; from = from->next)
        {
          if (from->node_type == PT_SPEC && PT_SPEC_IS_ENTITY (from))
        {
          (void) pt_resolve_spec_to_cte (parser, from, curr_cte, NULL);
          if (PT_SPEC_IS_CTE (from))
            {
              curr_cte_count++;
            }
        }
        }

      if (curr_cte_count == 0 && non_recursive_part->node_type == PT_SELECT)
        {
          /* no self references were found in arg2 from union; search in arg1 also, syntax allows this case */
          for (from = non_recursive_part->info.query.q.select.from; from != NULL; from = from->next)
        {
          if (from->node_type == PT_SPEC && PT_SPEC_IS_ENTITY (from))
            {
              (void) pt_resolve_spec_to_cte (parser, from, curr_cte, NULL);
              if (PT_SPEC_IS_CTE (from))
            {
              curr_cte_count++;
            }
            }
        }

          if (curr_cte_count > 0)
        {
          /* curr_cte is recursive but it was found in non-recursive part; swap non-recursive, recursive */
          PT_NODE *tmp = non_recursive_part;
          non_recursive_part = recursive_part;
          recursive_part = tmp;
        }
        }

      if (curr_cte_count > 0)
        {
          curr_cte->info.cte.non_recursive_part = non_recursive_part;
          curr_cte->info.cte.recursive_part = recursive_part;
          curr_cte->info.cte.only_all = all_distinct;
          if (curr_cte->info.cte.only_all != PT_ALL)
        {
          PT_ERRORmf (parser, curr_cte, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INCORRECT_UNION_IN_CTE,
                  curr_cte->info.cte.name->info.name.original);
          return NULL;
        }
        }

      /* check if there are any unresolved self references of the cte, it would be incorrect */
      curr_cte_count = 0;
      curr_cte = parser_walk_tree (parser, curr_cte, pt_resolve_spec_to_cte_and_count, curr_cte,
                       pt_count_ctes_post, &curr_cte_count);
      if (curr_cte == NULL)
        {
          /* error in walk */
          return NULL;
        }
      if (curr_cte_count > 0)
        {
          PT_ERRORmf (parser, curr_cte, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INCORRECT_RECURSIVE_CTE,
              curr_cte->info.cte.name->info.name.original);
          return NULL;
        }

      /* restore next link */
      curr_cte->next = saved_curr_cte_next;
    }

      curr_cte->info.cte.non_recursive_part->info.query.is_subquery = PT_IS_CTE_NON_REC_SUBQUERY;
      if (curr_cte->info.cte.recursive_part)
    {
      assert (curr_cte->info.cte.recursive_part->node_type == PT_SELECT);
      curr_cte->info.cte.recursive_part->info.query.is_subquery = PT_IS_CTE_REC_SUBQUERY;
    }
    }

  /* STEP 2: Resolve CTEs references within CTEs */
  for (previous_cte = cte_list, curr_cte = previous_cte->next; curr_cte != NULL;
       previous_cte = previous_cte->next, curr_cte = curr_cte->next)
    {
      /* disconect curr_cte from the list of CTEs that can be referenced in curr_cte */
      previous_cte->next = NULL;

      /* avoid following next links in parser_walk_tree */
      saved_curr_cte_next = curr_cte->next;
      curr_cte->next = NULL;

      /* cte_list keeps only the CTEs that precede curr_cte; resolve their references in curr_cte */
      curr_cte = parser_walk_tree (parser, curr_cte, pt_resolve_spec_to_cte, cte_list, NULL, NULL);
      if (curr_cte == NULL)
    {
      /* we expect error to be set */
      return NULL;
    }

      /* reconnect list, previous->next was curr_cte */
      previous_cte->next = curr_cte;

      /* restore next node */
      curr_cte->next = saved_curr_cte_next;
    }

  /* STEP 3: Resolve CTEs in the actual query */
  saved_with = *with_p;
  *with_p = NULL;
  node = parser_walk_tree (parser, node, pt_resolve_spec_to_cte, cte_list, NULL, NULL);
  *with_p = saved_with;

  /* all ok */
  return node;
}

/*
 * pt_copy_data_type_entity () -
 *   return: A copy of the entity name represented by the data type
 *   parser(in): parser environment
 *   data_type(in): data type node
 */
static PT_NODE *
pt_copy_data_type_entity (PARSER_CONTEXT * parser, PT_NODE * data_type)
{
  PT_NODE *entity = NULL;
  if (data_type->node_type == PT_DATA_TYPE)
    {
      if (data_type->info.data_type.virt_object)
    {
      entity = pt_name (parser, db_get_class_name (data_type->info.data_type.virt_object));
      entity->info.name.db_object = data_type->info.data_type.virt_object;
    }
      else
    {
      entity = parser_copy_tree_list (parser, data_type->info.data_type.entity);
    }
    }

  return entity;
}

/*
 * pt_insert_entity() -
 *   return: pointer to the entity spec, the parse tree is augmented with
 *           the new entity spec if one is created.
 *   parser(in): parser environment
 *   path(in): expression that caused the generation of this entity_spec
 *   prev_entity(in): the previous entity in the path expression
 *   correlation_entity(in):
 */

PT_NODE *
pt_insert_entity (PARSER_CONTEXT * parser, PT_NODE * path, PT_NODE * prev_entity, PT_NODE * correlation_entity)
{
  PT_NODE *entity = NULL;
  PT_NODE *res = NULL, *res1;
  PT_NODE *arg1;
  PT_NODE *arg1_name = NULL;
  PT_NODE *node;

  assert (path != NULL && prev_entity != NULL && path->node_type == PT_DOT_ && prev_entity->node_type == PT_SPEC);

  entity = pt_lookup_entity (parser, prev_entity->info.spec.path_entities, path);
  /* compute res */
  arg1 = path->info.dot.arg1;
  if ((arg1->node_type == PT_NAME) || (arg1->node_type == PT_METHOD_CALL))
    {
      res = arg1->data_type;
      arg1_name = arg1;     /* what about method calls with selectors? */
      res1 = NULL;
    }
  else
    {
      res = arg1->info.dot.arg2->data_type;
      arg1_name = arg1->info.dot.arg2;
      res1 = arg1->data_type;
    }

  if (!res || !(res->node_type == PT_DATA_TYPE) || !res->info.data_type.entity)
    {
      /* if we have a path, it must be from a known entity list */
      PT_INTERNAL_ERROR (parser, "resolution");
      return NULL;
    }

  if (entity == NULL)
    {
      /* create new entity spec if we have a correlation spec, use it. Otherwise, make a new one */
      if (correlation_entity)
    {
      entity = parser_copy_tree (parser, correlation_entity);
      entity->info.spec.derived_table_type = (PT_MISC_TYPE) 0;
      entity->info.spec.meta_class = PT_PATH_INNER;
      parser_free_tree (parser, correlation_entity->info.spec.path_entities);
      correlation_entity->info.spec.path_entities = NULL;
    }
      else
    {
      if (!(entity = parser_new_node (parser, PT_SPEC)))
        {
          PT_ERROR (parser, path,
            msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC,
                    MSGCAT_SEMANTIC_OUT_OF_MEMORY));
          return NULL;
        }

      /* use addr as entity_spec_id per convention */
      entity->info.spec.id = (UINTPTR) entity;
      entity->line_number = path->info.dot.arg2->line_number;
      entity->column_number = path->info.dot.arg2->column_number;

      /* mark if it is a pseudo entity for a method */
      if (arg1->node_type == PT_METHOD_CALL)
        {
          entity->info.spec.flavor = PT_METHOD_ENTITY;
        }

      entity->info.spec.entity_name = pt_copy_data_type_entity (parser, res);

      if (entity->info.spec.entity_name)
        {
          entity->info.spec.entity_name->info.name.spec_id = entity->info.spec.id;
          entity->info.spec.entity_name->info.name.meta_class = PT_CLASS;
          entity->info.spec.only_all = PT_ALL;
          entity->info.spec.range_var = parser_copy_tree (parser, entity->info.spec.entity_name);
          entity->info.spec.range_var->info.name.resolved = NULL;
          entity->info.spec.flat_entity_list = pt_make_flat_list_from_data_types (parser, res, entity);
        }

      /* We need to resolve the data type list to the newly created spec. */
      for (node = res; node; node = node->next)
        {
          if (node->info.data_type.entity)
        {
          node->info.data_type.entity->info.name.spec_id = entity->info.spec.id;
        }
        }

      /* The type of the DOT node is the same as the type of arg2 and so it must be resolved as well. */
      for (node = res1; node; node = node->next)
        {
          if (node->info.data_type.entity)
        {
          node->info.data_type.entity->info.name.spec_id = entity->info.spec.id;
        }
        }
    }

      /* add entity into path_entities list */
      entity->next = prev_entity->info.spec.path_entities;
      prev_entity->info.spec.path_entities = entity;
      entity = mq_regenerate_if_ambiguous (parser, entity, entity, entity);
      /* add the implicit conjunct */
      (void) pt_insert_conjunct (parser, path, entity);
    }
  if (correlation_entity && entity->info.spec.id != correlation_entity->info.spec.id)
    {
      const char *root = "";    /* safety net */
      if (arg1_name && arg1_name->node_type == PT_NAME)
    {
      root = arg1_name->info.name.original;
    }

      PT_ERRORmf3 (parser, path, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INCONSISTENT_PATH,
           pt_short_print (parser, path), pt_short_print (parser, correlation_entity->info.spec.range_var),
           root);
    }

  /* Make sure the arg2 points to the correct entity */
  path->info.dot.arg2->info.name.spec_id = entity->info.spec.id;
  path->info.dot.arg2->info.name.meta_class = PT_NORMAL;
  return entity;
}


/*
 * pt_insert_conjunct() -
 *   return: parse tree that is augmented with the new conjunct
 *   parser(in): parser environment
 *   path_dot(in): path expression that caused this implicit conjunct
 *   prev_entity(in): the previous entity in the path expression on which
 *                    we'll hang the new conjunct
 */

static PT_NODE *
pt_insert_conjunct (PARSER_CONTEXT * parser, PT_NODE * path_dot, PT_NODE * prev_entity)
{
  PT_NODE *conjunct;
  PT_NODE *arg1;
  PT_NODE *conj_name;
  PT_NODE *conj_res = NULL;

  assert (path_dot != NULL && path_dot->node_type == PT_DOT_ && prev_entity != NULL
      && prev_entity->node_type == PT_SPEC);

  arg1 = path_dot->info.dot.arg1;
  if ((arg1->node_type == PT_NAME) || (arg1->node_type == PT_METHOD_CALL))
    {
      conj_name = arg1;
    }
  else
    {
      conj_name = arg1->info.dot.arg2;
      /* comput conj_res to get exposed name from class data type of arg1 */
      arg1 = arg1->info.dot.arg1;
      if ((arg1->node_type == PT_NAME) || (arg1->node_type == PT_METHOD_CALL))
    {
      conj_res = arg1->data_type;
    }
      else
    {
      conj_res = arg1->info.dot.arg2->data_type;
    }
      if (conj_res)
    {
      conj_res = conj_res->info.data_type.entity;
    }
    }

  /* create new conjunct */
  if (!(conjunct = parser_new_node (parser, PT_EXPR)))
    {
      PT_ERROR (parser, path_dot,
        msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY));
      return NULL;
    }
  conjunct->info.expr.op = PT_EQ;
  conjunct->info.expr.arg1 = parser_copy_tree (parser, conj_name);
  if (conjunct->info.expr.arg1 == NULL)
    {
      PT_INTERNAL_ERROR (parser, "parser_copy_tree");
      return NULL;
    }

  if (conj_name->node_type == PT_METHOD_CALL)
    {
      conjunct->info.expr.arg1->info.method_call.method_name->info.name.spec_id = 0;
    }
  if (conj_res)
    {
      conjunct->info.expr.arg1->info.name.resolved = conj_res->info.name.original;
    }

  conjunct->info.expr.arg2 = parser_copy_tree (parser, prev_entity->info.spec.flat_entity_list);
  if (conjunct->info.expr.arg2 == NULL)
    {
      PT_INTERNAL_ERROR (parser, "parser_copy_tree");
      return NULL;
    }

  conjunct->info.expr.arg2->info.name.resolved = conjunct->info.expr.arg2->info.name.original;
  conjunct->info.expr.arg2->info.name.original = "";
  conjunct->info.expr.arg2->info.name.meta_class = PT_OID_ATTR;
  conjunct->info.expr.arg2->info.name.db_object = NULL;
  conjunct->info.expr.arg2->type_enum = conjunct->info.expr.arg1->type_enum;
  conjunct->info.expr.arg2->data_type = parser_copy_tree_list (parser, conjunct->info.expr.arg1->data_type);
  conjunct->line_number = path_dot->line_number;
  conjunct->column_number = path_dot->column_number;
  /* add conjunct into path_conjuncts list */
  conjunct->next = prev_entity->info.spec.path_conjuncts;
  prev_entity->info.spec.path_conjuncts = conjunct;
  return conjunct;
}               /* pt_insert_conjunct */


/*
 * pt_lookup_entity () -
 *   return: entity we are looking for
 *   parser(in):
 *   path_entities(in): entity list to look for entity
 *   expr(in):
 */
static PT_NODE *
pt_lookup_entity (PARSER_CONTEXT * parser, PT_NODE * path_entities, PT_NODE * expr)
{
  PT_NODE *entity;
  int found = false;
  PT_NODE *arg1_of_conj;
  const char *name = NULL, *cname;

  for (entity = path_entities; (entity != NULL && !found);)
    {               /* do nothing during increment step */

      arg1_of_conj = entity->info.spec.path_conjuncts->info.expr.arg1;
      if (expr->info.dot.arg1->node_type == PT_METHOD_CALL)
    {
      return NULL;
    }
      else
    {
      if (expr->info.dot.arg1->node_type == PT_DOT_)
        {
          name = expr->info.dot.arg1->info.dot.arg2->info.name.original;
        }
      else if (expr->info.dot.arg1->node_type == PT_NAME)
        {
          name = expr->info.dot.arg1->info.name.original;
        }

      if (arg1_of_conj->node_type == PT_NAME)
        {
          cname = arg1_of_conj->info.name.original;
          if (name != NULL && intl_identifier_casecmp (name, cname) == 0)
        {
          found = true;
        }
        }
    }

      if (!found)
    {
      entity = entity->next;
    }
    }

  return entity;
}



/*
 * pt_resolve_object () - gets the object to update either from a host var
 *      or from a parameter.  Once it has the object it creates an entity for
 *      the object
 *   return: none
 *   parser(in):
 *   node(in): an PT_UPDATE_INFO node representing an UPDATE OBJECT.. statement
 */
void
pt_resolve_object (PARSER_CONTEXT * parser, PT_NODE * node)
{
  DB_VALUE *val = NULL;
  PT_NODE *entity;
  DB_OBJECT *class_op = NULL;
  PT_NODE *obj_param = node->info.update.object_parameter;
  assert (obj_param != NULL);
  if (obj_param->node_type == PT_NAME)
    {
      if (obj_param->info.name.meta_class != PT_PARAMETER)
    {
      PT_INTERNAL_ERROR (parser, "resolution");
      return;
    }

      /* look it up as an interpreter parameter reference */
      val = pt_find_value_of_label (obj_param->info.name.original);
      if (!val)         /* parameter not found */
    {
      PT_ERRORmf (parser, obj_param, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_IS_NOT_DEFINED,
              obj_param->info.name.original);
      return;
    }
    }
  else if (obj_param->node_type == PT_HOST_VAR)
    {
      val = pt_value_to_db (parser, obj_param);
    }

  if (val == NULL)      /* parameter not found */
    {
      PT_ERRORm (parser, obj_param, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UNDEFINED_ARGUMENT);
      return;
    }

  if (DB_VALUE_TYPE (val) != DB_TYPE_OBJECT)
    {
      PT_ERRORm (parser, obj_param, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ARG_IS_NOT_AN_OBJECT);
      return;
    }
  node->info.update.object = db_get_object (val);
  class_op = db_get_class (node->info.update.object);
  if (class_op == NULL)
    {
      PT_ERRORm (parser, obj_param, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_UNDEFINED_ARGUMENT);
      return;
    }

  /* create an entity */
  entity = parser_new_node (parser, PT_SPEC);
  if (entity == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return;
    }

  entity->info.spec.id = (UINTPTR) entity;
  entity->line_number = node->line_number;
  entity->column_number = node->column_number;
  entity->info.spec.entity_name = parser_new_node (parser, PT_NAME);
  if (entity->info.spec.entity_name == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return;
    }

  entity->info.spec.entity_name->info.name.spec_id = entity->info.spec.id;
  entity->info.spec.entity_name->info.name.meta_class = PT_CLASS;
  entity->info.spec.entity_name->info.name.original = db_get_class_name (class_op);
  entity->info.spec.only_all = PT_ONLY;
  entity->info.spec.range_var = parser_copy_tree (parser, entity->info.spec.entity_name);
  if (entity->info.spec.range_var == NULL)
    {
      PT_INTERNAL_ERROR (parser, "parser_copy_tree");
      return;
    }

  entity->info.spec.range_var->info.name.resolved = NULL;
  node->info.update.spec = entity;
}


/*
 * pt_resolve_method_type () - resolves the return type of the method call
 *      and creates a data_type for it if it is needed
 *   return:
 *   parser(in):
 *   node(in): an PT_METHOD_CALL node
 */
static bool
pt_resolve_method_type (PARSER_CONTEXT * parser, PT_NODE * node)
{
  PT_NODE *target;
  const char *method_name;
  DB_OBJECT *class_op;
  DB_METHOD *method = NULL;
  DB_DOMAIN *dom = NULL;
  if ((!node->info.method_call.method_name) || (!(target = node->info.method_call.on_call_target))
      || (!target->data_type) || (!target->data_type->info.data_type.entity)
      || (!(class_op = target->data_type->info.data_type.entity->info.name.db_object)))
    {
      return false;     /* not a method */
    }
  method_name = node->info.method_call.method_name->info.name.original;
  method = NULL;
  method = db_get_method (class_op, method_name);
  if (method == NULL)
    {
      /* need to check if it is a class method */
      if (er_errid () == ER_OBJ_INVALID_METHOD)
    {
      er_clear ();
    }
      method = db_get_class_method (class_op, method_name);
      if (method == NULL)
    {
      return false;     /* not a method */
    }
      node->info.method_call.method_type = PT_IS_CLASS_MTHD;
    }
  else
    {
      node->info.method_call.method_type = PT_IS_INST_MTHD;
    }

  /* look up the domain of the method's return type */
  if ((dom = db_method_arg_domain (method, 0)) == NULL)
    {
      /* only give error if it is a method expression */
      if (node->info.method_call.call_or_expr != PT_IS_CALL_STMT)
    {
      PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_METH_NOT_TYPED, method_name);
    }
      return false;
    }

  node->type_enum = pt_db_to_type_enum (TP_DOMAIN_TYPE (dom));
  switch (node->type_enum)
    {
    case PT_TYPE_OBJECT:
    case PT_TYPE_SET:
    case PT_TYPE_SEQUENCE:
    case PT_TYPE_MULTISET:
    case PT_TYPE_NUMERIC:
    case PT_TYPE_BIT:
    case PT_TYPE_VARBIT:
    case PT_TYPE_CHAR:
    case PT_TYPE_VARCHAR:
      node->data_type = pt_domain_to_data_type (parser, dom);
      break;
    default:
      break;
    }

  /* finally resolve method id */
  node->info.method_call.method_id = (UINTPTR) node;
  return true;
}               /* pt_resolve_method_type */



/*
 * pt_make_method_call () - creates a PT_METHOD_CALL node and initilaizes from PT_FUNCTION node
 *   return:
 *   parser(in):
 *   f_node(in): an PT_FUNCTION node that may really be a method call
 *   bind_arg(in):
 */
static PT_NODE *
pt_make_method_call (PARSER_CONTEXT * parser, PT_NODE * f_node, PT_BIND_NAMES_ARG * bind_arg)
{
  PT_NODE *new_node;

  /* initialize the new node with the corresponding fields from the PT_FUNCTION node. */
  new_node = parser_new_node (parser, PT_METHOD_CALL);
  if (new_node == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

  new_node->info.method_call.method_name = parser_new_node (parser, PT_NAME);
  if (new_node->info.method_call.method_name == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return NULL;
    }

  PT_NODE_COPY_NUMBER_OUTERLINK (new_node, f_node);

  new_node->info.method_call.method_name->info.name.original = f_node->info.function.generic_name;
  new_node->info.method_call.method_name->info.name.spec_id = (UINTPTR) new_node->info.method_call.method_name;
  new_node->info.method_call.method_name->info.name.meta_class = PT_METHOD;
  new_node->info.method_call.arg_list = parser_copy_tree_list (parser, f_node->info.function.arg_list);
  new_node->info.method_call.call_or_expr = PT_IS_MTHD_EXPR;
  new_node->info.method_call.on_call_target = NULL;
  new_node->info.method_call.auth_name = NULL;
  PT_METHOD_CALL_AUTH_NAME (new_node) = ws_copy_string (au_get_user_name (Au_user));    // default

  return new_node;
}               /* pt_make_method_call */

/*
 * pt_resolve_method () - creates and resolves the PT_METHOD_CALL node for method
 *   return:
 *   parser(in):
 *   node(in): an PT_FUNCTION node that may really be a method call
 *   bind_arg(in):
 */
static PT_NODE *
pt_resolve_method (PARSER_CONTEXT * parser, PT_NODE * node, PT_BIND_NAMES_ARG * bind_arg)
{
  PT_NODE *new_node = pt_make_method_call (parser, node, bind_arg);
  if (new_node == NULL)
    {
      return NULL;
    }

  if (new_node->info.method_call.arg_list == NULL)
    {
      return node;      /* return the function since it is not a method */
    }

  /* The first argument (which must be present), is the target of the method.  Move it to the on_call_target. */
  new_node->info.method_call.on_call_target = new_node->info.method_call.arg_list;
  new_node->info.method_call.arg_list = new_node->info.method_call.arg_list->next;
  new_node->info.method_call.on_call_target->next = NULL;

  /* bind the names in the method arguments and target, their scope will be the same as the method node's scope */
  parser_walk_leaves (parser, new_node, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);

  /* find the type of the method here */
  if (!pt_resolve_method_type (parser, new_node))
    {
      parser_free_node (parser, new_node->info.method_call.method_name);
      parser_free_node (parser, new_node);
      return node;      /* not a method call */
    }
  else
    {
      /* if scopes is NULL we assume this came from an evaluate call and we treat it like a call statement, that
       * is, we don't resolve method name. */
      if (bind_arg->scopes == NULL)
    {
      return new_node;
    }
      /* resolve method name to entity where expansion will take place */
      if ((new_node->info.method_call.on_call_target->node_type == PT_NAME)
      && (new_node->info.method_call.on_call_target->info.name.meta_class != PT_PARAMETER))
    {
      PT_NODE *entity, *spec;
      entity = NULL;    /* init */
      if (PT_IS_CLASS_METHOD (node))
        {
          for (spec = bind_arg->spec_frames->extra_specs; spec != NULL; spec = spec->next)
        {
          if (spec->node_type == PT_SPEC
              && (spec->info.spec.id == new_node->info.method_call.on_call_target->info.name.spec_id))
            {
              entity = spec;
              break;
            }
        }
        }
      else
        {
          entity =
        pt_find_entity_in_scopes (parser, bind_arg->scopes,
                      new_node->info.method_call.on_call_target->info.name.spec_id);
        }
      /* no entity found will be caught as an error later.  Probably an unresolvable target. */
      if (entity)
        {
          new_node->info.method_call.method_name->info.name.spec_id = entity->info.spec.id;
        }
    }

      new_node->info.method_call.method_name->info.name.meta_class = PT_METHOD;
      return new_node;      /* it is a method call */
    }
}               /* pt_resolve_method */

/*
 * pt_resolve_stored_procedure () - creates and resolves the PT_METHOD_CALL node for java sp
 *   return:
 *   parser(in):
 *   node(in): an PT_FUNCTION node that may really be a method call
 *   bind_arg(in):
 */
static PT_NODE *
pt_resolve_stored_procedure (PARSER_CONTEXT * parser, PT_NODE * node, PT_BIND_NAMES_ARG * bind_arg)
{
  char owner[DB_MAX_USER_LENGTH + 1];
  owner[0] = '\0';
  char *current_user_owner;

  PT_NODE *new_node = pt_make_method_call (parser, node, bind_arg);
  if (new_node == NULL)
    {
      return NULL;
    }

  parser_walk_leaves (parser, new_node, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);

  /* returns either error or DB_TYPE... */
  const char *sp_name = new_node->info.method_call.method_name->info.name.original;
  int return_type = jsp_get_return_type (sp_name);
  if (return_type < 0)
    {
      PT_INTERNAL_ERROR (parser, "jsp_get_return_type");
      return NULL;
    }

  if (jsp_get_owner_name (sp_name, owner, DB_MAX_USER_LENGTH) == NULL)
    {
      PT_INTERNAL_ERROR (parser, "jsp_get_owner_name");
      return NULL;
    }

  new_node->type_enum = pt_db_to_type_enum ((DB_TYPE) return_type);
  TP_DOMAIN *d = pt_type_enum_to_db_domain (new_node->type_enum);
  d = tp_domain_cache (d);
  new_node->data_type = pt_domain_to_data_type (parser, d);

  new_node->info.method_call.method_id = (UINTPTR) new_node;

  PT_MISC_TYPE sp_type_misc = (PT_MISC_TYPE) jsp_get_sp_type (sp_name);
  // stored procedures can only be invoked through a CALL statement
  if (sp_type_misc == PT_SP_PROCEDURE &&
      (bind_arg->sc_info->top_node == NULL || bind_arg->sc_info->top_node->node_type != PT_METHOD_CALL))
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_STORED_PROC_CALL_IN_SQL);
      return NULL;
    }
  new_node->info.method_call.method_type = sp_type_misc;

  PT_METHOD_CALL_AUTH_ID (new_node) = PT_AUTHID_OWNER;  // TODO
  if (PT_METHOD_CALL_AUTH_ID (new_node) == PT_AUTHID_OWNER)
    {
      PT_METHOD_CALL_AUTH_NAME (new_node) = pt_append_string (parser, NULL, owner);
    }
  else
    {
      current_user_owner = au_get_user_name (Au_user);
      PT_METHOD_CALL_AUTH_NAME (new_node) = pt_append_string (parser, NULL, current_user_owner);
      ws_free_string_and_init (current_user_owner);
    }

  return new_node;
}               /* pt_resolve_stored_procedure */

/*
 * pt_find_entity_in_scopes () - looks up an entity spec in a scope list
 *   return:
 *   parser(in):
 *   scopes(in):
 *   spec_id(in):
 */
static PT_NODE *
pt_find_entity_in_scopes (PARSER_CONTEXT * parser, SCOPES * scopes, UINTPTR spec_id)
{
  SCOPES *scope;
  PT_NODE *entity = NULL;
  for (scope = scopes; scope != NULL; scope = scope->next)
    {
      entity = pt_find_entity (parser, scope->specs, spec_id);
      if (entity)
    {
      break;
    }
    }

  return entity;
}

/*
 * pt_find_outer_entity_in_scopes () -
 *   return:
 *   parser(in):
 *   scopes(in):
 *   spec_id(in):
 *   scope_location(in):
 */
static PT_NODE *
pt_find_outer_entity_in_scopes (PARSER_CONTEXT * parser, SCOPES * scopes, UINTPTR spec_id, short *scope_location)
{
  PT_NODE *spec, *temp;
  int location = 0;
  if (scopes == NULL)
    {
      PT_INTERNAL_ERROR (parser, "resolution");
      return NULL;
    }


  for (spec = scopes->specs; spec; spec = spec->next)
    {
      if (spec->node_type != PT_SPEC)
    {
      PT_INTERNAL_ERROR (parser, "resolution");
      return NULL;
    }
      if (spec->info.spec.join_type == PT_JOIN_NONE)
    {
      location = spec->info.spec.location;
    }
      if (spec->info.spec.id == spec_id)
    {
      for (temp = spec; temp && temp->info.spec.location < *scope_location; temp = temp->next)
        {
          if (temp->info.spec.join_type == PT_JOIN_NONE)
        location = temp->info.spec.location;
        }
      *scope_location = location;
      return spec;
    }
    }

  return NULL;
}


/*
 * pt_make_flat_list_from_data_types
 *
 * description: For each node in the res_list, this routine appends that node's
 *      entity list onto the flat list that is being created.  The resulting
 *      flat list is then normalized (correct spec_id, etc.)
 */

/*
 * pt_make_flat_list_from_data_types () - For each node in the res_list,
 *      this routine appends that node's entity list onto the flat list that
 *      is being created. The resulting flat list is then normalized
 *   return:
 *   parser(in):
 *   res_list(in):
 *   entity(in):
 */
static PT_NODE *
pt_make_flat_list_from_data_types (PARSER_CONTEXT * parser, PT_NODE * res_list, PT_NODE * entity)
{
  PT_NODE *node, *temp, *flat_list = NULL;
  for (node = res_list; node; node = node->next)
    {
      temp = pt_copy_data_type_entity (parser, node);
      flat_list = pt_name_list_union (parser, flat_list, temp);
    }

  /* set all ids on flat list */
  for (node = flat_list; node != NULL; node = node->next)
    {
      node->info.name.spec_id = entity->info.spec.id;
      node->info.name.resolved = entity->info.spec.entity_name->info.name.original;
      if (PT_IS_SPEC_REAL_TABLE (entity))
    {
      PT_NAME_INFO_SET_FLAG (node, PT_NAME_DEFAULTF_ACCEPTS);
    }
      node->info.name.meta_class = PT_CLASS;
    }

  return flat_list;
}

/*
 * pt_op_type_from_default_expr_type () - returns the corresponding PT_OP_TYPE
 *                    for the given default expression
 *   return: a PT_OP_TYPE (the desired operation)
 *   expr_type(in): a DB_DEFAULT_EXPR_TYPE (the default expression)
 */
PT_OP_TYPE
pt_op_type_from_default_expr_type (DB_DEFAULT_EXPR_TYPE expr_type)
{
  switch (expr_type)
    {
    case DB_DEFAULT_SYSTIME:
      return PT_SYS_TIME;

    case DB_DEFAULT_SYSDATE:
      return PT_SYS_DATE;

    case DB_DEFAULT_SYSDATETIME:
      return PT_SYS_DATETIME;

    case DB_DEFAULT_SYSTIMESTAMP:
      return PT_SYS_TIMESTAMP;

    case DB_DEFAULT_UNIX_TIMESTAMP:
      return PT_UNIX_TIMESTAMP;

    case DB_DEFAULT_USER:
      return PT_USER;

    case DB_DEFAULT_CURR_USER:
      return PT_CURRENT_USER;

    case DB_DEFAULT_CURRENTDATETIME:
      return PT_CURRENT_DATETIME;

    case DB_DEFAULT_CURRENTTIMESTAMP:
      return PT_CURRENT_TIMESTAMP;

    case DB_DEFAULT_CURRENTTIME:
      return PT_CURRENT_TIME;

    case DB_DEFAULT_CURRENTDATE:
      return PT_CURRENT_DATE;

    default:
      return (PT_OP_TYPE) 0;
    }
}

DB_OBJECT *
pt_resolve_serial (PARSER_CONTEXT * parser, PT_NODE * node)
{
  DB_OBJECT *serial_class_obj = NULL;
  DB_OBJECT *serial_obj = NULL;
  DB_IDENTIFIER serial_obj_id;
  const char *serial_name = NULL;
  const char *serial_unique_name = NULL;
  const char *owner_name = NULL;

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

  if (PT_IS_DOT_NODE (node))
    {
      assert (PT_IS_NAME_NODE (node->info.dot.arg1));
      assert (PT_IS_NAME_NODE (node->info.dot.arg2));

      owner_name = node->info.dot.arg1->info.name.original;
      serial_name = node->info.dot.arg2->info.name.original;
    }
  else if (PT_IS_NAME_NODE (node))
    {
      serial_name = node->info.name.original;
    }
  else
    {
      return NULL;
    }

  if (serial_name == NULL || serial_name[0] == '\0')
    {
      return NULL;
    }

  if (owner_name && owner_name[0] != '\0')
    {
      serial_unique_name = pt_append_string (parser, owner_name, ".");
    }
  serial_unique_name = pt_append_string (parser, NULL, serial_name);

  serial_class_obj = sm_find_class (CT_SERIAL_NAME);
  serial_obj = do_get_serial_obj_id (&serial_obj_id, serial_class_obj, serial_unique_name);
  if (serial_obj == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_QPROC_SERIAL_NOT_FOUND, 1, serial_unique_name);
    }

  return serial_obj;
}

/*
 * pt_function_name_is_spec_attr () - checks if a generic function name is
 *  actually an attribute name. It is used to distinguish between an error
 *  caused by the wrong usage of a prefix length index or an function
 *      index.
 *
 *   return: NO_ERROR on success, non-zero for ERROR
 *   parser(in): parser context
 *   node(in): PT_FUNCTION node holding the function name to be searched
 *   bind_arg(in): list of scopes
 *   is_spec_attr(out): 1 if it is actually an attribute name, 0 otherwise
 */
static int
pt_function_name_is_spec_attr (PARSER_CONTEXT * parser, PT_NODE * node, PT_BIND_NAMES_ARG * bind_arg, int *is_spec_attr)
{
  SCOPES *scope = NULL;
  PT_NODE *spec = NULL;
  PT_NODE *attr = NULL;

  assert (node->node_type == PT_FUNCTION);

  *is_spec_attr = 0;
  attr = pt_name (parser, node->info.function.generic_name);
  if (attr == NULL)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      return ER_FAILED;
    }

  /* walk scopes (might be unnecessary) */
  for (scope = bind_arg->scopes; scope; scope = scope->next)
    {
      /* walk specs */
      for (spec = scope->specs; spec; spec = spec->next)
    {
      if (pt_find_name_in_spec (parser, spec, attr) > 0)
        {
          *is_spec_attr = 1;
          break;
        }
    }
      if (*is_spec_attr == 1)
    {
      /* no need to continue */
      break;
    }
    }

  if (attr != NULL)
    {
      parser_free_node (parser, attr);
    }

  return NO_ERROR;
}

/*
 * pt_mark_function_index_expression () - mark function index expression
 *   return:
 *   parser(in): parser context
 *   node(in): PT_EXPR node
 *   bind_arg(in): list of scopes
 */
static void
pt_mark_function_index_expression (PARSER_CONTEXT * parser, PT_NODE * expr, PT_BIND_NAMES_ARG * bind_arg)
{
  SCOPES *scope = NULL;
  PT_NODE *spec = NULL;
  MOP cls;
  SM_CLASS_CONSTRAINT *constraints;
  char *expr_str = NULL;
  PT_NODE *flat = NULL;

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

  /* walk scopes */
  for (scope = bind_arg->scopes; scope; scope = scope->next)
    {
      /* walk specs */
      for (spec = scope->specs; spec; spec = spec->next)
    {
      if (spec->info.spec.meta_class == PT_CLASS && !spec->info.spec.derived_table
          && spec->info.spec.flat_entity_list)
        {
          flat = spec->info.spec.flat_entity_list;
          while (flat)
        {
          if (flat->node_type != PT_NAME)
            {
              PT_INTERNAL_ERROR (parser, "resolution");
              return;
            }

          /* Get the object */
          cls = flat->info.name.db_object;
          if (!cls)
            {
              PT_INTERNAL_ERROR (parser, "resolution");
              return;
            }

          constraints = sm_class_constraints (cls);
          while (constraints != NULL)
            {
              if (constraints->func_index_info)
            {
              if (expr_str == NULL)
                {
                  expr_str = parser_print_function_index_expr (parser, expr);
                }

              if (!intl_identifier_casecmp (expr_str, constraints->func_index_info->expr_str))
                {
                  PT_EXPR_INFO_SET_FLAG (expr, PT_EXPR_INFO_FUNCTION_INDEX);
                  return;
                }
            }
              constraints = constraints->next;
            }

          flat = flat->next;
        }
        }
    }
    }
}

/*
 * pt_bind_names_merge_insert () -
 *   return:
 *   parser(in):
 *   node(in):
 *   bind_arg(in):
 *   scopestack(in):
 *   spec_frame(in):
 */
static void
pt_bind_names_merge_insert (PARSER_CONTEXT * parser, PT_NODE * node, PT_BIND_NAMES_ARG * bind_arg,
                SCOPES * scopestack, PT_EXTRA_SPECS_FRAME * spec_frame)
{
  PT_NODE *temp_node, *node_list, *save_next, *prev_node = NULL;
  bool is_first_node;

  assert (node->node_type == PT_MERGE);

  /* bind names for insert attributes list */
  scopestack->specs = node->info.merge.into;
  bind_arg->scopes = scopestack;
  spec_frame->next = bind_arg->spec_frames;
  spec_frame->extra_specs = NULL;
  bind_arg->spec_frames = spec_frame;
  pt_bind_scope (parser, bind_arg);
  node->info.merge.insert.attr_list =
    parser_walk_tree (parser, node->info.merge.insert.attr_list, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);
  if (pt_has_error (parser))
    {
      return;
    }

  /* bind names for default function in insert values list */
  node_list = node->info.merge.insert.value_clauses->info.node_list.list;
  for (temp_node = node_list, is_first_node = true; temp_node != NULL;
       temp_node = temp_node->next, is_first_node = false)
    {
      if (temp_node->node_type == PT_EXPR && temp_node->info.expr.op == PT_DEFAULTF)
    {
      scopestack->specs = node->info.merge.into;
      bind_arg->scopes = scopestack;
      spec_frame->next = bind_arg->spec_frames;
      spec_frame->extra_specs = NULL;
      bind_arg->spec_frames = spec_frame;
      pt_bind_scope (parser, bind_arg);

      save_next = temp_node->next;
      temp_node->next = NULL;
      temp_node = parser_walk_tree (parser, temp_node, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);
      temp_node->next = save_next;

      if (is_first_node)
        {
          node->info.merge.insert.value_clauses->info.node_list.list = temp_node;
        }
      else
        {
          prev_node->next = temp_node;
        }
    }
      prev_node = temp_node;
    }

  if (pt_has_error (parser))
    {
      return;
    }

  /* bind names for the rest of insert values list */
  scopestack->specs = node->info.merge.using_clause;
  bind_arg->scopes = scopestack;
  spec_frame->next = bind_arg->spec_frames;
  spec_frame->extra_specs = NULL;
  bind_arg->spec_frames = spec_frame;
  pt_bind_scope (parser, bind_arg);
  node->info.merge.insert.value_clauses =
    parser_walk_tree (parser, node->info.merge.insert.value_clauses, pt_bind_names, bind_arg, pt_bind_names_post,
              bind_arg);

  if (pt_has_error (parser))
    {
      return;
    }

  /* bind names for insert search condition */
  scopestack->specs = node->info.merge.using_clause;
  bind_arg->scopes = scopestack;
  spec_frame->next = bind_arg->spec_frames;
  spec_frame->extra_specs = NULL;
  bind_arg->spec_frames = spec_frame;
  pt_bind_scope (parser, bind_arg);
  node->info.merge.insert.search_cond =
    parser_walk_tree (parser, node->info.merge.insert.search_cond, pt_bind_names, bind_arg, pt_bind_names_post,
              bind_arg);
}

/*
 * pt_bind_names_merge_update () -
 *   return:
 *   parser(in):
 *   node(in):
 *   bind_arg(in):
 *   scopestack(in):
 *   spec_frame(in):
 */
static void
pt_bind_names_merge_update (PARSER_CONTEXT * parser, PT_NODE * node, PT_BIND_NAMES_ARG * bind_arg,
                SCOPES * scopestack, PT_EXTRA_SPECS_FRAME * spec_frame)
{
  PT_NODE *assignment;

  /* resolve lhs with target spec only */
  scopestack->specs = node->info.merge.into;
  bind_arg->scopes = scopestack;
  spec_frame->next = bind_arg->spec_frames;
  spec_frame->extra_specs = NULL;
  bind_arg->spec_frames = spec_frame;
  pt_bind_scope (parser, bind_arg);
  for (assignment = node->info.merge.update.assignment; assignment; assignment = assignment->next)
    {
      if (PT_IS_N_COLUMN_UPDATE_EXPR (assignment->info.expr.arg1))
    {
      assignment->info.expr.arg1->info.expr.arg1 =
        parser_walk_tree (parser, assignment->info.expr.arg1->info.expr.arg1, pt_bind_names, bind_arg,
                  pt_bind_names_post, bind_arg);
      if (assignment->info.expr.arg1->info.expr.arg1 == NULL)
        {
          assert (pt_has_error (parser));
          return;
        }
    }
      else
    {
      assignment->info.expr.arg1 =
        parser_walk_tree (parser, assignment->info.expr.arg1, pt_bind_names, bind_arg, pt_bind_names_post,
                  bind_arg);
      if (assignment->info.expr.arg1 == NULL)
        {
          assert (pt_has_error (parser));
          return;
        }
    }
    }

  /* resolve rhs with both source and target specs */
  scopestack->specs = node->info.merge.into;
  node->info.merge.into->next = node->info.merge.using_clause;
  bind_arg->scopes = scopestack;
  spec_frame->next = bind_arg->spec_frames;
  spec_frame->extra_specs = NULL;
  bind_arg->spec_frames = spec_frame;
  pt_bind_scope (parser, bind_arg);
  for (assignment = node->info.merge.update.assignment; assignment; assignment = assignment->next)
    {
      assignment->info.expr.arg2 =
    parser_walk_tree (parser, assignment->info.expr.arg2, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);
      if (assignment->info.expr.arg2 == NULL)
    {
      assert (pt_has_error (parser));
      return;
    }
    }
  node->info.merge.into->next = NULL;
}

/*
 * pt_resolve_partition_spec - resolve a spec specified with PARTITION keyword
 *                 to the actual partition spec
 * return: partition spec
 * parser (in) : parser context
 * spec (in)   : spec
 * spec_parent (in) : spec parent
 * for_update (in): is update purpose?
 */
static PT_NODE *
pt_resolve_partition_spec (PARSER_CONTEXT * parser, PT_NODE * spec, PT_NODE * spec_parent, bool for_update)
{
  const char *partition_suffix = NULL;
  const char *partition_name = NULL;
  DB_OBJECT *root_op, *partition_op = NULL;
  SM_CLASS *class_ = NULL;
  PT_NODE *entity_name = NULL, *partition_node = NULL;
  const char *root_name = NULL;
  AU_FETCHMODE fetchmode;

  if (spec == NULL || spec->info.spec.partition == NULL)
    {
      return spec;
    }

  entity_name = spec->info.spec.entity_name;
  root_name = entity_name->info.name.original;
  partition_node = spec->info.spec.partition;
  partition_suffix = partition_node->info.name.original;
  partition_name = pt_partition_name (parser, root_name, partition_suffix);
  if (partition_name == NULL)
    {
      return NULL;
    }

  /* set the actual partition name to the partition node */
  partition_node->info.name.original = partition_name;

  /* use partition name to make flat list */
  spec->info.spec.entity_name = partition_node;
  spec->info.spec.partition = NULL;
  spec->info.spec.flat_entity_list = pt_make_flat_name_list (parser, spec, spec_parent, for_update);
  if (spec->info.spec.flat_entity_list == NULL)
    {
      return NULL;
    }

  /* Verify if current spec is a partition of the saved entity name */
  root_op = db_find_class_with_purpose (root_name, for_update);
  if (root_op == NULL)
    {
      PT_ERRORc (parser, spec, er_msg ());
      return NULL;
    }

  partition_op = spec->info.spec.entity_name->info.name.db_object;
  if (partition_op == NULL)
    {
      /* We have successfully resolved it, it should not be null */
      PT_INTERNAL_ERROR (parser, "resolution");
      return NULL;
    }

  fetchmode = for_update ? AU_FETCH_UPDATE : AU_FETCH_READ;

  /* do not check authorization here */
  if (au_fetch_class_force (partition_op, &class_, fetchmode) != NO_ERROR)
    {
      PT_ERRORc (parser, spec, er_msg ());
      return NULL;
    }

  if (class_->partition == NULL)
    {
      /* no partition information */
      PT_ERRORmf (parser, spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_PARTITION_DOES_NOT_EXIST, partition_suffix);
      return NULL;
    }

  if (class_->users != NULL)
    {
      /* this is a partitioned class, it cannot be a partition of the specified class */
      PT_ERRORmf (parser, spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_PARTITION_DOES_NOT_EXIST, partition_suffix);
      return NULL;
    }

  /* class_ is a partition, we only have to verify that its superclass is root_op */
  if (class_->inheritance->op != root_op)
    {
      /* class_ is a partition of a different class */
      PT_ERRORmf (parser, spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_PARTITION_DOES_NOT_EXIST, partition_suffix);
      return NULL;
    }

  /* set root class name as alias for this spec */
  if (spec->info.spec.range_var == NULL)
    {
      spec->info.spec.range_var = entity_name;
      entity_name = NULL;
    }

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

  return spec;
}

/*
 * pt_set_reserved_name_key_type () - When scanning for index key and node
 *                    info, keys are selected from index.
 *                    This function is supposed to resolve
 *                    data type for such attributes.
 *
 * return         : Original node with updated type_enum.
 * parser (in)        : Parser context.
 * node (in)          : Parse tree node.
 * arg (in)       : Index key data type.
 * continue_walk (in) : Continue walk.
 */
static PT_NODE *
pt_set_reserved_name_key_type (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_BIND_NAMES_DATA_TYPE *key_type = (PT_BIND_NAMES_DATA_TYPE *) arg;

  if (node != NULL && node->node_type == PT_NAME && node->info.name.meta_class == PT_RESERVED
      && (node->info.name.reserved_id == RESERVED_KEY_KEY || node->info.name.reserved_id == RESERVED_BT_NODE_FIRST_KEY
      || node->info.name.reserved_id == RESERVED_BT_NODE_LAST_KEY))
    {
      /* Set key type */
      node->data_type = parser_copy_tree_list (parser, key_type->data_type);
      if (node->data_type == NULL)
    {
      PT_ERRORm (parser, node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      *continue_walk = PT_STOP_WALK;
    }
      node->type_enum = key_type->type_enum;
    }
  return node;
}

/*
 * pt_bind_names_in_with_clause () - resolve names in with clause of node
 *
 * return             : void
 * parser (in)        : Parser context.
 * node (in)          : Parse tree node.
 * bind_arg (in)      : Bind names arg.
 *
 * Note: The names from the with clause should be resolved before resolving the actual query;
 *       Since CTEs may be referenced multiple times in a query, better resolve them separately
 *       instead of resolving each occurence
 */
static void
pt_bind_names_in_with_clause (PARSER_CONTEXT * parser, PT_NODE * node, PT_BIND_NAMES_ARG * bind_arg)
{
  PT_NODE *with = NULL;
  PT_NODE *curr_cte;

  switch (node->node_type)
    {
    case PT_SELECT:
    case PT_UNION:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
      with = node->info.query.with;
      break;
    case PT_UPDATE:
      with = node->info.update.with;
      break;
    case PT_DELETE:
      with = node->info.delete_.with;
      break;
    default:
      assert (false);
    }

  if (with == NULL)
    {
      /* nothing to do */
      return;
    }

  for (curr_cte = with->info.with_clause.cte_definition_list; curr_cte != NULL; curr_cte = curr_cte->next)
    {
      /* isolate curr_cte and apply resolve names */
      PT_NODE *save_next_cte = curr_cte->next;
      curr_cte->next = NULL;

      pt_bind_names_in_cte (parser, curr_cte, bind_arg);

      curr_cte->next = save_next_cte;
      if (pt_has_error (parser))
    {
      return;
    }
    }
}


/*
 * pt_resolve_names_in_cte - resolve names in cte definition
 *
 * return             : void
 * parser (in)        : Parser context.
 * node (in)          : Parse tree node.
 * bind_arg (in)      : Bind names arg.
 */
static void
pt_bind_names_in_cte (PARSER_CONTEXT * parser, PT_NODE * cte_def, PT_BIND_NAMES_ARG * bind_arg)
{
  PT_NODE *save_next_cte_def;
  PT_NODE *non_recursive_cte = cte_def->info.cte.non_recursive_part;
  PT_NODE *recursive_cte = cte_def->info.cte.recursive_part;
  bool save_donot_fold;

  assert (cte_def->node_type == PT_CTE);

  if (non_recursive_cte == NULL)
    {
      /* something went wrong, this shouldn't be possible */
      assert (0);
      return;
    }

  /* Evaluate the non-recursive part:
   *    bind the names and the types from the non-recursive part and set cte->as_attr_list accordingly;
   *    the recursive part(if it exists) will use the as_attr_list to bind its own names and types;
   */
  non_recursive_cte = parser_walk_tree (parser, non_recursive_cte, pt_bind_names, bind_arg,
                    pt_bind_names_post, bind_arg);

  /* must bind any expr types in non recursive part; required for types binding */
  save_donot_fold = bind_arg->sc_info->donot_fold;  /* save */
  bind_arg->sc_info->donot_fold = true; /* skip folding */
  non_recursive_cte = pt_semantic_type (parser, non_recursive_cte, bind_arg->sc_info);
  bind_arg->sc_info->donot_fold = save_donot_fold;  /* restore */
  if (non_recursive_cte == NULL)
    {
      goto end;
    }

  /* build the cte as_attr_list according to the non_recursive part */
  if (cte_def->info.cte.as_attr_list == NULL)
    {
      cte_def->info.cte.as_attr_list =
    pt_get_attr_list_of_derived_table (parser, PT_IS_SUBQUERY, non_recursive_cte, cte_def->info.cte.name);
    }
  pt_set_attr_list_types (parser, cte_def->info.cte.as_attr_list, PT_IS_SUBQUERY, non_recursive_cte, cte_def);

  /* done with the non_recursive_part */
  cte_def->info.cte.non_recursive_part = non_recursive_cte;

  /* Evaluate the recursive part:
   *    bind the names and the types of the recursive part, and unify them with the ones of the non-recursive part
   *    pt_semantic_type will unify the types of the two parts considering that there is a UNION between them
   *    cte->as_attr_list will be overwritten with the unified types
   */
  if (recursive_cte != NULL)
    {
      recursive_cte = parser_walk_tree (parser, recursive_cte, pt_bind_names, bind_arg, pt_bind_names_post, bind_arg);
      if (recursive_cte == NULL)
    {
      goto end;
    }

      cte_def->info.cte.recursive_part = recursive_cte;

      /* must bind any expr types in cte; required for types binding */
      save_donot_fold = bind_arg->sc_info->donot_fold;
      bind_arg->sc_info->donot_fold = true;
      save_next_cte_def = cte_def->next;
      cte_def->next = NULL;
      cte_def = pt_semantic_type (parser, cte_def, bind_arg->sc_info);
      if (!cte_def)
    {
      return;       /* error in pt_semantic_type */
    }

      bind_arg->sc_info->donot_fold = save_donot_fold;  /* restore */
      cte_def->next = save_next_cte_def;

      /* set final CTE attributes types */
      pt_set_attr_list_types (parser, cte_def->info.cte.as_attr_list, PT_IS_SUBQUERY, recursive_cte, cte_def);

      /* the attributes of cte self references specs can now be computed correctly */
      recursive_cte = parser_walk_tree (parser, recursive_cte, pt_bind_cte_self_references_types, NULL, NULL, NULL);
    }

end:
  cte_def->info.cte.non_recursive_part = non_recursive_cte;
  cte_def->info.cte.recursive_part = recursive_cte;
}

/*
 * pt_bind_cte_self_references_types () - used to bind types of self reference specs in recursive cte part;
 *                    this can be done only after bind_names is complete and
 *                    final cte attributes types are set
 *   return:
 *   parser(in):
 *   node(in):
 *   arg(in/out):
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_bind_cte_self_references_types (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  if (node == NULL || node->node_type != PT_SPEC || !PT_SPEC_IS_CTE (node))
    {
      /* interested only in CTE specs */
      return node;
    }

  pt_bind_spec_attrs (parser, node);

  *continue_walk = PT_LIST_WALK;

  return node;
}


/*
 * pt_get_attr_list_of_derived_table - determine the list of aliases of a derived table
 *
 *  return : Computed as_attr_list
 *  parser (in) : Parser context.
 *  derived_table_type (in) : Provided by the spec that contains the derived table.
 *  derived_table (in) : Derived table to be processed.
 *  derived_alias (in) : The alias of the derived table (also comes from the spec).
 */
static PT_NODE *
pt_get_attr_list_of_derived_table (PARSER_CONTEXT * parser, PT_MISC_TYPE derived_table_type,
                   PT_NODE * derived_table, PT_NODE * derived_alias)
{
  PT_NODE *as_attr_list = NULL, *select_list;
  unsigned int save_custom;
  int i, id;

  switch (derived_table_type)
    {
    case PT_IS_SET_EXPR:
      /* if derived from a set expression, it better have a set type */
      if (derived_table->node_type == PT_NAME && derived_table->info.name.original != NULL
      && derived_table->info.name.original[0] != '\0')
    {
      as_attr_list = pt_name (parser, derived_table->info.name.original);
    }
      else
    {           /* generate column name */
      id = 0;
      as_attr_list = pt_name (parser, mq_generate_name (parser, derived_alias->info.name.original, &id));
    }
      break;

    case PT_IS_SHOWSTMT:
      as_attr_list = pt_get_all_showstmt_attributes_and_types (parser, derived_table);
      if (as_attr_list == NULL)
    {
      PT_INTERNAL_ERROR (parser, "resolution");
      return NULL;
    }
      break;

    case PT_IS_SUBQUERY:
      /* must be a subquery derived table */
      /* select_list must have passed star expansion */
      PT_NODE * att, *col;

      select_list = pt_get_select_list (parser, derived_table);
      if (!select_list)
    {
      return NULL;
    }

      for (att = select_list, i = 0; att; att = att->next, i++)
    {
      if (att->alias_print)
        {
          col = pt_name (parser, att->alias_print);
        }
      else
        {
          if (att->node_type == PT_NAME && att->info.name.original != NULL && att->info.name.original[0] != '\0')
        {
          col = pt_name (parser, att->info.name.original);
        }
          else if (att->node_type == PT_VALUE && att->info.value.text != NULL && att->info.value.text[0] != '\0')
        {
          col = pt_name (parser, att->info.value.text);
        }
          else if (att->node_type == PT_EXPR || att->node_type == PT_FUNCTION)
        {
          PARSER_VARCHAR *alias;
          save_custom = parser->custom_print;
          parser->custom_print |= PT_PRINT_NO_SPECIFIED_USER_NAME;
          alias = pt_print_bytes (parser, att);
          parser->custom_print = save_custom;
          col = pt_name (parser, (const char *) alias->bytes);
        }
          else
        {       /* generate column name */
          id = i;
          col = pt_name (parser, mq_generate_name (parser, derived_alias->info.name.original, &id));
        }
        }

      col->type_enum = att->type_enum;
      if (att->data_type)
        {
          col->data_type = parser_copy_tree_list (parser, att->data_type);
        }

      as_attr_list = parser_append_node (col, as_attr_list);
    }
      break;

    case PT_DERIVED_JSON_TABLE:
      assert (derived_table->node_type == PT_JSON_TABLE);

      as_attr_list = pt_get_all_json_table_attributes_and_types (parser, derived_table->info.json_table_info.tree,
                                 derived_alias->info.name.original);
      break;

    case PT_DERIVED_DBLINK_TABLE:
      assert (derived_table->node_type == PT_DBLINK_TABLE);

      as_attr_list = pt_get_all_dblink_table_attributes_and_types (parser, derived_table->info.dblink_table.cols,
                                   derived_alias->info.name.original);
      break;

    default:
      /* this can't happen since we removed MERGE/CSELECT from grammar */
      assert (derived_table_type == PT_IS_CSELECT);
      PT_INTERNAL_ERROR (parser, "resolution");
      return NULL;
    }

  return as_attr_list;
}

/*
 * pt_set_attr_list_types - identify and set the types of attributes
 *
 *  return : void
 *  parser (in) : Parser context.
 *  as_attr_list(in/out) : attr list to be processed.
 *  derived_table_type (in) : Provided by the spec that contains the derived table.
 *  derived_table (in) : Derived table to be processed.
 *  parent_spec (in) : The spec where derived table resides.
 */
static void
pt_set_attr_list_types (PARSER_CONTEXT * parser, PT_NODE * as_attr_list, PT_MISC_TYPE derived_table_type,
            PT_NODE * derived_table, PT_NODE * parent_spec)
{
  PT_NODE *elt_type, *select_list;
  int col_cnt = pt_length_of_list (as_attr_list), attr_cnt;

  switch (derived_table_type)
    {
    case PT_IS_SET_EXPR:
      /* if derived from a set expression, it better have a set type */
      if (!pt_is_set_type (derived_table))
    {
      PT_ERRORmf (parser, parent_spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_MUST_BE_SET_MSET_SEQ,
              pt_short_print (parser, derived_table));
      return;
    }

      elt_type = derived_table->data_type;

      /* Handle a derived table query on an empty set stored in a host variable (elt_type==NULL) the same way as a NULL
       * value.  Note that derived table queries on sets of anything will also result in a null elt_type here */
      if (PT_IS_NULL_NODE (derived_table) || !elt_type)
    {
      /* this is to accept a NULL literal value consistently with NULL column values. Unfortunaltely, no type
       * information for the column may be deduced. */
      as_attr_list->type_enum = PT_TYPE_NULL;
      as_attr_list->data_type = NULL;
    }
      else
    {
      /*
       * Make sure that all elements on the data type list have the
       * same type_enum, e.g., that they all represent classes.
       */
      PT_TYPE_ENUM t = elt_type->type_enum;
      PT_NODE *p;
      for (p = elt_type->next; p; p = p->next)
        {
          if (p->type_enum != t)
        {
          elt_type = NULL;
          break;
        }
        }
      if (!elt_type)
        {
          PT_ERRORmf (parser, parent_spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_GT_1_SET_ELEM_TYPE,
              pt_short_print (parser, derived_table));
          return;
        }

      /* it should have exactly one derived column */
      if (col_cnt > 1)
        {
          PT_ERRORmf (parser, parent_spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_GT_1_DERIVED_COL_NAM,
              pt_short_print_l (parser, as_attr_list));
          return;
        }
      if (col_cnt < 1)
        {
          PT_ERRORmf (parser, parent_spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_NO_DERIVED_COL_NAM,
              pt_short_print (parser, derived_table));
          return;
        }

      /* derived column assumes the set's element type */
      as_attr_list->type_enum = elt_type->type_enum;
      as_attr_list->data_type = parser_copy_tree_list (parser, elt_type);
    }

      break;


    case PT_IS_SHOWSTMT:
      {
    /* types are already set in pt_get_all_showstmt_attributes_and_types; */
    PT_NODE *col = as_attr_list;
    while (col)
      {
        col->line_number = parent_spec->line_number;
        col->column_number = parent_spec->column_number;
        col = col->next;
      }
    return;
      }

    case PT_IS_SUBQUERY:
      /* must be a subquery derived table */
      /* select_list must have passed star expansion */
      select_list = pt_get_select_list (parser, derived_table);
      if (!select_list)
    {
      return;
    }

      /* select_list attributes must match derived columns in number */
      attr_cnt = pt_length_of_select_list (select_list, INCLUDE_HIDDEN_COLUMNS);
      if (col_cnt != attr_cnt)
    {
      PT_ERRORmf3 (parser, parent_spec, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_ATT_CNT_NE_DERIVED_C,
               pt_short_print (parser, parent_spec), attr_cnt, col_cnt);
      return;
    }
      else
    {
      PT_NODE *col, *att;
      /* derived columns assume the type of their matching attributes */
      for (col = as_attr_list, att = select_list; col && att; col = col->next, att = att->next)
        {
          col->type_enum = att->type_enum;
          if (att->data_type)
        {
          col->data_type = parser_copy_tree_list (parser, att->data_type);
        }
          else
        {
          parser_free_tree (parser, col->data_type);
          col->data_type = NULL;
        }
        }
    }
      break;

    case PT_DERIVED_JSON_TABLE:
      // nothing to do? Types already set during pt_json_table_gather_attribs ()
      return;

    case PT_DERIVED_DBLINK_TABLE:
      // nothing to do? Types already set during pt_dblink_table_gather_attribs ()
      return;

    default:
      /* this can't happen since we removed MERGE/CSELECT from grammar */
      assert (derived_table_type == PT_IS_CSELECT);
      PT_INTERNAL_ERROR (parser, "resolution");
      return;
    }
}

/*
 * pt_count_with_clauses - find if node has WITH clause
 *   return:
 *   parser(in):
 *   node(in): the node to check
 *   arg(out): count of WITH clauses
 *   continue_walk(in):
 */
static PT_NODE *
pt_count_with_clauses (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  int *cnt = (int *) arg;

  if (node != NULL && PT_IS_QUERY_NODE_TYPE (node->node_type) && node->info.query.with != NULL)
    {
      /* node has WITH clause */
      (*cnt)++;
    }

  return node;
}

static PT_NODE *
pt_bind_name_to_spec (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE *spec = REINTERPRET_CAST (PT_NODE *, arg);
  if (spec->node_type != PT_SPEC)
    {
      assert (false);
      return node;
    }

  if (node->node_type != PT_NAME)
    {
      // not relevant
      return node;
    }

  if (pt_resolved (node))
    {
      assert (node->info.name.spec_id == spec->info.spec.id);
      return node;
    }

  node->info.name.spec_id = spec->info.spec.id;
  node->info.name.resolved = spec->info.spec.range_var->info.name.original;
  node->info.name.meta_class = PT_NORMAL;   // so far, only normals are used.
  return node;
}

static int
pt_resolve_dblink_server_name (PARSER_CONTEXT * parser, PT_NODE * node, char **server_owner_name)
{
  PT_NODE *val[3];
  DB_VALUE values[4];
  PT_DBLINK_INFO *dblink_table = &node->info.dblink_table;
  int i, error;

  assert (dblink_table->conn && dblink_table->conn->node_type == PT_NAME);

  db_make_null (&(values[0]));
  db_make_null (&(values[1]));
  db_make_null (&(values[2]));
  db_make_null (&(values[3]));

  error = get_dblink_info_from_dbserver (parser, dblink_table->conn, dblink_table->owner_name, values);
  if (error != NO_ERROR)
    {
      // TODO: error handling
      if (er_errid_if_has_error () != NO_ERROR)
    {
      PT_ERROR (parser, node, (char *) er_msg ());
    }
      else if (!pt_has_error (parser))
    {
      if (dblink_table->owner_name)
        {
          PT_ERRORf3 (parser, node, "Failed to obtain server information for [%s].[%s]. error=%d",
              dblink_table->owner_name->info.name.original, dblink_table->conn->info.name.original, error);
        }
      else
        {
          PT_ERRORf2 (parser, node, "Failed to obtain server information for [%s]. error=%d",
              dblink_table->conn->info.name.original, error);
        }
    }

      pr_clear_value (&(values[0]));
      pr_clear_value (&(values[1]));
      pr_clear_value (&(values[2]));
      pr_clear_value (&(values[3]));

      return error;
    }

  for (i = 0; i < 3; i++)
    {
      val[i] = parser_new_node (parser, PT_VALUE);
      if (val[i] == NULL)
    {
      if (!pt_has_error (parser))
        {
          PT_ERROR (parser, node, "allocation error");
        }

      while (--i >= 0)
        {
          parser_free_node (parser, val[i]);
        }

      return ER_FAILED;
    }

      val[i]->type_enum = PT_TYPE_CHAR;
      val[i]->info.value.string_type = ' ';
    }

  dblink_table->url = val[0];
  dblink_table->user = val[1];
  dblink_table->pwd = val[2];

  char *url, *username, *password;

  url = (char *) db_get_string (&(values[0]));
  username = (char *) db_get_string (&(values[1]));
  password = (char *) db_get_string (&(values[2]));

  if (server_owner_name)
    {
      PARSER_VARCHAR *vc;
      char *ownername = (char *) db_get_string (&(values[3]));
      vc = pt_append_nulstring (parser, NULL, ownername);
      *server_owner_name = (char *) vc->bytes;
    }

  dblink_table->url->info.value.data_value.str = pt_append_nulstring (parser, NULL, url);
  dblink_table->user->info.value.data_value.str = pt_append_nulstring (parser, NULL, username);
  dblink_table->pwd->info.value.data_value.str = pt_append_nulstring (parser, NULL, (password ? password : ""));

  pr_clear_value (&(values[0]));
  pr_clear_value (&(values[1]));
  pr_clear_value (&(values[2]));
  pr_clear_value (&(values[3]));

  return NO_ERROR;
}

static int
pt_resolve_dblink_check_owner_name (PARSER_CONTEXT * parser, PT_NODE * node, char **server_owner_name)
{
  DB_VALUE value;
  int i, error;

  assert (node->node_type == PT_NAME);

  db_make_null (&value);

  error = get_dblink_owner_name_from_dbserver (parser, node, node->next, &value);
  if (error != NO_ERROR)
    {
      // TODO: error handling
      if (er_errid_if_has_error () != NO_ERROR)
    {
      PT_ERROR (parser, node, (char *) er_msg ());
    }
      else if (!pt_has_error (parser))
    {
      if (node->next)
        {
          PT_ERRORf3 (parser, node, "Failed to obtain server information for [%s].[%s]. error=%d",
              node->next->info.name.original, node->info.name.original, error);
        }
      else
        {
          PT_ERRORf2 (parser, node, "Failed to obtain server information for [%s]. error=%d",
              node->info.name.original, error);
        }
    }

      pr_clear_value (&value);
      return error;
    }

  if (server_owner_name)
    {
      PARSER_VARCHAR *vc;
      char *ownername = (char *) db_get_string (&value);
      vc = pt_append_nulstring (parser, NULL, ownername);
      *server_owner_name = (char *) vc->bytes;
    }

  pr_clear_value (&value);

  return NO_ERROR;
}

static const char *
pt_print_pl_host_expr (PARSER_CONTEXT * parser, PT_NODE * node)
{
  if (PT_IS_NAME_NODE (node))
    {
      assert (node->info.name.original);
      return node->info.name.original;
    }
  else if (PT_IS_DOT_NODE (node))
    {
      PT_NODE *rec = node->info.dot.arg1;
      PT_NODE *field = node->info.dot.arg2;
      if (PT_IS_NAME_NODE (rec) && PT_IS_NAME_NODE (field))
    {
      assert (rec->info.name.original);
      assert (field->info.name.original);

      PARSER_VARCHAR *q = NULL;
      q = pt_append_nulstring (parser, q, rec->info.name.original);
      q = pt_append_nulstring (parser, q, ".");
      q = pt_append_nulstring (parser, q, field->info.name.original);
      return (const char *) q->bytes;
    }
    }

  return NULL;
}

static PT_NODE *
pt_parameterize_for_static_sql (PARSER_CONTEXT * parser, PT_NODE * name_node)
{
  PT_NODE *hostvar = parser_new_node (parser, PT_HOST_VAR);
  hostvar->info.host_var.str = pt_append_string (parser, NULL, "?");

  const char *host_expr_str = pt_print_pl_host_expr (parser, name_node);
  if (!host_expr_str)
    {
      unsigned int saved_custom = parser->custom_print;
      parser->custom_print = PT_SUPPRESS_RESOLVED | PT_SUPPRESS_QUOTES;
      char *err = parser_print_tree (parser, name_node);
      parser->custom_print = saved_custom;
      PT_ERRORmf (parser, name_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_INVALID_HOST_EXPR, err);
      return NULL;
    }
  hostvar->info.host_var.label = host_expr_str;
  hostvar->info.host_var.var_type = PT_HOST_IN;
  hostvar->info.host_var.index = parser->host_var_count;
  hostvar->type_enum = PT_TYPE_NONE;

  // Expand parser->host_variables by realloc
  int count_to_realloc = parser->host_var_count + 1;

  /* We actually allocate around twice more than needed so that we don't do useless copies too often. */
  count_to_realloc = (count_to_realloc / 2) * 4;

  if (count_to_realloc == 0)
    {
      count_to_realloc = 1;
    }

  DB_VALUE *larger_host_variables = (DB_VALUE *) realloc (parser->host_variables, count_to_realloc * sizeof (DB_VALUE));
  if (larger_host_variables == NULL)
    {
      PT_ERRORm (parser, name_node, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_OUT_OF_MEMORY);
      return NULL;
    }

  TP_DOMAIN **larger_host_var_expected_domains =
    (TP_DOMAIN **) realloc (parser->host_var_expected_domains, count_to_realloc * sizeof (TP_DOMAIN *));

  parser->host_variables = larger_host_variables;
  parser->host_var_expected_domains = larger_host_var_expected_domains;

  db_make_null (&parser->host_variables[parser->host_var_count]);
  parser->host_var_expected_domains[parser->host_var_count] = pt_type_enum_to_db_domain (PT_TYPE_NONE);

  ++parser->host_var_count;
  larger_host_variables = NULL;
  larger_host_var_expected_domains = NULL;

  PT_NODE_MOVE_NUMBER_OUTERLINK (hostvar, name_node);
  parser_free_tree (parser, name_node);

  return hostvar;
}

typedef struct link_columns
{
  PT_NODE *col_list;
  PT_NODE *tbl_name_node;
} S_LINK_COLUMNS;


static void
check_for_already_exists (PARSER_CONTEXT * parser, S_LINK_COLUMNS * plkcol, const char *resolved, const char *original)
{
  const char *tbl_alias_nm = plkcol->tbl_name_node->info.name.original;
  PT_NODE *col;

  if (resolved && intl_identifier_casecmp (tbl_alias_nm, resolved) != 0)
    {
      return;
    }

  if (plkcol->col_list)
    {
      if (plkcol->col_list->type_enum == PT_TYPE_STAR && plkcol->col_list->info.name.resolved == NULL)
    {
      return;       // case: * vs anything
    }

      if (resolved == NULL)
    {           // col
      for (col = plkcol->col_list; col; col = col->next)
        {
          if (col->type_enum == PT_TYPE_STAR)
        {
          return;   // case: tbl.* vs anything
        }
          else if (intl_identifier_casecmp (col->info.name.original, original) == 0)
        {
          return;   // case: col  vs col
        }
        }
    }
      else if (original)
    {           // tbl.col
      for (col = plkcol->col_list; col; col = col->next)
        {
          if (col->type_enum == PT_TYPE_STAR)
        {
          return;   // case: tbl.* vs anything
        }
          else if (intl_identifier_casecmp (col->info.name.original, original) == 0)
        {
          return;   // case: col  vs col  or  col vs tbl.col
        }
        }
    }
      else
    {           // tbl.*
      if (plkcol->col_list->type_enum == PT_TYPE_STAR)
        {
          return;       // case: tbl.* vs tbl.*
        }

      parser_free_node (parser, plkcol->col_list);
      plkcol->col_list = NULL;
    }
    }

  PT_NODE *name = parser_new_node (parser, PT_NAME);

  if (resolved && original)
    {
      //name->info.name.resolved = pt_append_string (parser, NULL, resolved);
      name->info.name.original = pt_append_string (parser, NULL, original);
    }
  else if (resolved)
    {
      name->type_enum = PT_TYPE_STAR;
      name->info.name.resolved = pt_append_string (parser, NULL, resolved);
    }
  else
    {
      name->info.name.original = pt_append_string (parser, NULL, original);
    }

  plkcol->col_list = parser_append_node (name, plkcol->col_list);
}


static PT_NODE *
pt_get_column_name_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  S_LINK_COLUMNS *plkcol = (S_LINK_COLUMNS *) arg;
  PT_NODE *name = NULL;

  switch (node->node_type)
    {
    case PT_DOT_:
      check_for_already_exists (parser, plkcol, node->info.dot.arg1->info.name.original,
                node->info.dot.arg2->info.name.original);
      break;

    case PT_NAME:
      if (node->type_enum == PT_TYPE_STAR)
    {           // case:  tbl.*
      check_for_already_exists (parser, plkcol, node->info.name.original, NULL);
    }
      else
    {
      check_for_already_exists (parser, plkcol, NULL, node->info.name.original);
    }
      break;

    case PT_VALUE:
      if (node->type_enum == PT_TYPE_STAR)
    {
      name = parser_new_node (parser, PT_NAME);
      name->type_enum = PT_TYPE_STAR;
      if (plkcol->col_list)
        {
          parser_free_node (parser, plkcol->col_list);
        }
      plkcol->col_list = name;
    }
      break;

    default:
      break;
    }

  return node;
}

static void
pt_get_cols_for_dblink (PARSER_CONTEXT * parser, S_LINK_COLUMNS * plkcol, PT_QUERY_INFO * query, PT_NODE * on_cond)
{
  if (query->q.select.list)
    {
      (void) parser_walk_tree (parser, query->q.select.list, pt_get_column_name_pre, plkcol, NULL, NULL);
      if (plkcol->col_list && plkcol->col_list->type_enum == PT_TYPE_STAR)
    {
      return;
    }
    }
  if (on_cond)
    {
      (void) parser_walk_tree (parser, on_cond, pt_get_column_name_pre, plkcol, NULL, NULL);
    }
  if (query->q.select.where)
    {
      (void) parser_walk_tree (parser, query->q.select.where, pt_get_column_name_pre, plkcol, NULL, NULL);
    }
  if (query->q.select.having)
    {
      (void) parser_walk_tree (parser, query->q.select.having, pt_get_column_name_pre, plkcol, NULL, NULL);
    }
  if (query->q.select.group_by)
    {
      (void) parser_walk_tree (parser, query->q.select.group_by, pt_get_column_name_pre, plkcol, NULL, NULL);
    }
  if (query->q.select.connect_by)
    {
      (void) parser_walk_tree (parser, query->q.select.connect_by, pt_get_column_name_pre, plkcol, NULL, NULL);
    }
  if (query->q.select.start_with)
    {
      (void) parser_walk_tree (parser, query->q.select.start_with, pt_get_column_name_pre, plkcol, NULL, NULL);
    }
  if (query->q.select.after_cb_filter)
    {
      (void) parser_walk_tree (parser, query->q.select.after_cb_filter, pt_get_column_name_pre, plkcol, NULL, NULL);
    }
  if (query->order_by)
    {
      (void) parser_walk_tree (parser, query->order_by, pt_get_column_name_pre, plkcol, NULL, NULL);
    }
  if (query->orderby_for)
    {
      (void) parser_walk_tree (parser, query->orderby_for, pt_get_column_name_pre, plkcol, NULL, NULL);
    }
}

static void
pt_gather_dblink_colums (PARSER_CONTEXT * parser, PT_NODE * query_stmt)
{
  PT_QUERY_INFO *query = &query_stmt->info.query;
  PT_NODE *table;
  PT_NODE *spec;

  for (spec = query->q.select.from; spec; spec = spec->next)
    {
      if (!PT_SPEC_IS_DERIVED (spec))
    {
      continue;
    }

      table = spec->info.spec.derived_table;
      if (table->node_type == PT_DBLINK_TABLE)
    {
      assert (spec->info.spec.derived_table_type == PT_DERIVED_DBLINK_TABLE);
      if (table->info.dblink_table.remote_table_name && *table->info.dblink_table.remote_table_name)
        {
          S_LINK_COLUMNS lkcol;

          memset (&lkcol, 0x00, sizeof (lkcol));
          lkcol.col_list = table->info.dblink_table.sel_list;

          lkcol.tbl_name_node = spec->info.spec.range_var;
          pt_get_cols_for_dblink (parser, &lkcol, query, spec->info.spec.on_cond);

          table->info.dblink_table.sel_list = lkcol.col_list;
          lkcol.col_list = NULL;
        }
    }
    }
}

PT_NODE *
pt_check_dblink_query (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  //*continue_walk = PT_CONTINUE_WALK;
  if (!node || !parser)
    {
      return node;
    }

  if (node->node_type == PT_SELECT)
    {
      pt_gather_dblink_colums (parser, node);
    }

  return node;
}

int
pt_check_dblink_column_alias (PARSER_CONTEXT * parser, PT_NODE * dblink)
{
  int cols_idx = 0;
  S_REMOTE_TBL_COLS *rmt_tbl_cols;
  PT_NODE *cols;
  char *col_name;

  rmt_tbl_cols = (S_REMOTE_TBL_COLS *) dblink->info.dblink_table.remote_col_list;
  if (rmt_tbl_cols == NULL)
    {
      PT_ERRORf (parser, dblink, "internal error, no remote columns", ER_DBLINK);
      return ER_FAILED;
    }

  if (rmt_tbl_cols->is_select_star ())
    {
      return NO_ERROR;
    }

  cols = dblink->info.dblink_table.cols;
  while (cols)
    {
      assert (cols_idx < rmt_tbl_cols->get_attr_size ());
      col_name = (char *) cols->info.attr_def.attr_name->info.name.original;
      if (intl_identifier_casecmp (rmt_tbl_cols->get_name (cols_idx), col_name) != 0)
    {
      PT_ERRORf3 (parser, dblink, "\"%s\" not matched column or alias \"%s\"",
              cols->info.attr_def.attr_name->info.name.original, rmt_tbl_cols->get_name (cols_idx), ER_DBLINK);
      return ER_FAILED;
    }

      cols = cols->next;
      cols_idx++;
    }

  return NO_ERROR;
}

void
pt_free_dblink_remote_cols (PARSER_CONTEXT * parser)
{
  REMOTE_COLS *remote = parser->dblink_remote;

  while (remote)
    {
      delete (S_REMOTE_TBL_COLS *) remote->cols;
      remote = remote->next;
    }
}