Skip to content

File execute_statement.c

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

/*
 * execute_statement.c - functions to do execute
 */

#ident "$Id$"

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#if defined(WINDOWS)
#include <process.h>        /* for getpid() */
#include <winsock2.h>       /* for struct timeval */
#else /* WINDOWS */
#include <unistd.h>     /* for getpid() */
#include <libgen.h>     /* for dirname, basename() */
#include <sys/time.h>       /* for struct timeval */
#endif /* WINDOWS */
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <stdarg.h>
#include <ctype.h>


#include "error_manager.h"
#include "db.h"
#include "dbi.h"
#include "dbtype.h"
#include "parser.h"
#include "porting.h"
#include "schema_manager.h"
#include "schema_system_catalog_constants.h"
#include "transform.h"
#include "parser_message.h"
#include "system_parameter.h"
#include "execute_statement.h"
#if defined(WINDOWS)
#include "misc_string.h"
#endif

#include "semantic_check.h"
#include "execute_schema.h"
#include "server_interface.h"
#include "transaction_cl.h"
#include "object_print.h"
#include "jansson.h"
#include "jsp_cl.h"
#include "optimizer.h"
#include "memory_alloc.h"
#include "object_domain.h"
#include "object_primitive.h"
#include "object_representation.h"
#include "trigger_manager.h"
#include "release_string.h"
#include "object_accessor.h"
#include "locator_cl.h"
#include "authenticate.h"
#include "xasl_generation.h"
#include "virtual_object.h"
#include "environment_variable.h"
#include "set_object.h"
#include "intl_support.h"
#include "replication.h"
#include "view_transform.h"
#include "network_interface_cl.h"
#include "arithmetic.h"
#include "xasl_to_stream.h"
#include "query_cl.h"
#include "parser_support.h"
#include "tz_support.h"
#include "dbtype.h"
#include "crypt_opfunc.h"
#include "method_callback.hpp"
#include "network.h"

#if defined (SUPPRESS_STRLEN_WARNING)
#define strlen(s1)  ((int) strlen(s1))
#endif /* defined (SUPPRESS_STRLEN_WARNING) */

#define DB_SERIAL_MAX "99999999999999999999999999999999999999"
#define DB_SERIAL_MIN "-99999999999999999999999999999999999999"

#define UNIQUE_SAVEPOINT_ALTER_TRIGGER "aLTERtRIGGER"

#define CUSTOM_PRINT_4_SHA_COMPUTE (PT_CONVERT_RANGE | PT_PRINT_QUOTES | PT_PRINT_USER | PT_PRINT_HOST_VAR_COUNT | PT_PRINT_DBLINK_INFO)
/*
 * Function Group:
 * Do create/alter/drop serial statement
 *
 */

#define PT_NODE_SR_NAME(node)           \
    ((node)->info.serial.serial_name->info.name.original)
#define PT_NODE_SR_START_VAL(node)      \
    ((node)->info.serial.start_val)
#define PT_NODE_SR_INCREMENT_VAL(node)      \
    ((node)->info.serial.increment_val)
#define PT_NODE_SR_MAX_VAL(node)        \
    ((node)->info.serial.max_val)
#define PT_NODE_SR_MIN_VAL(node)        \
    ((node)->info.serial.min_val)
#define PT_NODE_SR_CYCLIC(node)         \
    ((node)->info.serial.cyclic )
#define PT_NODE_SR_NO_MAX(node)         \
    ((node)->info.serial.no_max )
#define PT_NODE_SR_NO_MIN(node)         \
    ((node)->info.serial.no_min )
#define PT_NODE_SR_NO_CYCLIC(node)      \
    ((node)->info.serial.no_cyclic )
#define PT_NODE_SR_CACHED_NUM_VAL(node)     \
    ((node)->info.serial.cached_num_val)
#define PT_NODE_SR_NO_CACHE(node)       \
    ((node)->info.serial.no_cache)

static void do_set_trace_to_query_flag (QUERY_FLAG * query_flag);
static void do_send_plan_trace_to_session (PARSER_CONTEXT * parser);
static int do_vacuum (PARSER_CONTEXT * parser, PT_NODE * statement);
static int do_insert_checks (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE ** class_,
                 PT_NODE ** update, PT_NODE * values);

static int do_alter_synonym_internal (const char *synonym_name, const char *target_name, DB_OBJECT * target_owner,
                      const char *comment, const int is_public_synonym, bool is_dblinked);
static int do_create_synonym_internal (const char *synonym_name, DB_OBJECT * synonym_owner, const char *target_name,
                       DB_OBJECT * target_owner, const char *comment, const int is_public_synonym,
                       const int or_replace, bool is_dblinked);
static int do_drop_synonym_internal (const char *synonym_name, const int is_public_synonym, const int if_exists,
                     DB_OBJECT * synonym_class_obj, DB_OBJECT * synonym_obj);
static int do_rename_synonym_internal (const char *old_synonym_name, const char *new_synonym_name);
static PT_NODE *do_prepare_subquery_pre (PARSER_CONTEXT * parser, PT_NODE * stmt, void *arg, int *continue_walk);
#define MAX_SERIAL_INVARIANT    8
typedef struct serial_invariant SERIAL_INVARIANT;

/* an invariant which serial must hold */
struct serial_invariant
{
  DB_VALUE val1;
  DB_VALUE val2;
  PT_OP_TYPE cmp_op;
  int val1_msgid;       /* the proper message id for val1. 0 means val1 should not be responsible for the
                 * invariant violation */
  int val2_msgid;       /* the proper message id for val2. 0 means val2 should not be responsible for the
                 * invariant violation */
  int error_type;       /* ER_QPROC_SERIAL_RANGE_OVERFLOW or ER_INVALID_SERIAL_VALUE */
};

/*
 * eval_insert_value -
 * Structure is passed as argument to parser_walk_tree when insert values are
 * evaluated and stores the information required for evaluation.
 */
typedef struct eval_insert_value EVAL_INSERT_VALUE;
struct eval_insert_value
{
  UINTPTR spec_id;      /* insert spec_id */
  PT_NODE *attr_list;       /* list of insert attribute names */
  PT_NODE *value_list;      /* list of insert values values */
  int crt_attr_index;       /* current attribute index */
  bool reevaluate_needed;   /* currently evaluated insert value may need to be reevaluated with next execution */
  bool replace_names;       /* true if names may need to be replaced with each evaluation */
};

typedef struct reserved_class_info
{
  OID oid;
  CDC_DDL_OBJECT_TYPE objtype;
  char name[DB_MAX_IDENTIFIER_LENGTH];
} RESERVED_CLASS_INFO;

static void initialize_serial_invariant (SERIAL_INVARIANT * invariant, DB_VALUE val1, DB_VALUE val2,
                     PT_OP_TYPE cmp_op, int val1_msgid, int val2_msgid, int error_type);
static int check_serial_invariants (SERIAL_INVARIANT * invariants, int num_invariants, int *ret_msg_id);
static bool truncate_need_repl_log (PT_NODE * statement);
static int do_check_for_empty_classes_in_delete (PARSER_CONTEXT * parser, PT_NODE * statement);

static int do_evaluate_insert_values (PARSER_CONTEXT * parser, PT_NODE * insert_statement);
static void do_clear_insert_values (PARSER_CONTEXT * parser, PT_NODE * insert_statement);
static PT_NODE *do_replace_names_for_insert_values_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg,
                            int *continue_walk);
static int do_prepare_insert_internal (PARSER_CONTEXT * parser, PT_NODE * statement);
static int do_insert_template (PARSER_CONTEXT * parser, DB_OTMPL ** otemplate, PT_NODE * statement,
                   const char **savepoint_name, int *row_count_ptr);
static void init_compile_context (PARSER_CONTEXT * parser);

static int do_select_internal (PARSER_CONTEXT * parser, PT_NODE * statement, bool for_ins_upd);

static int get_dblink_password_encrypt (const char *passwd, DB_VALUE * encrypt_val);
static int get_dblink_password_decrypt (const char *passwd_cipher, DB_VALUE * decrypt_val);
static MOP server_find (PT_NODE * node_server, PT_NODE * node_owner);

static int do_supplemental_statement (PARSER_CONTEXT * parser, PT_NODE * statement, RESERVED_CLASS_INFO ** cls_info,
                      OID * reserved_oid);

static int do_reserve_classinfo (PARSER_CONTEXT * parser, PT_NODE * statement, RESERVED_CLASS_INFO ** cls_info);

static int do_reserve_oidinfo (PARSER_CONTEXT * parser, PT_NODE * statement, OID ** oid);
/*
 * initialize_serial_invariant() - initialize a serial invariant
 *   return: None
 *   invariant(out):
 *   val1(in):
 *   val2(in):
 *   cmp_op(in):
 *   val1_msgid(in):
 *   val2_msgid(in):
 *   error_type(in):
 *
 * Note:
 */
static void
initialize_serial_invariant (SERIAL_INVARIANT * invariant, DB_VALUE val1, DB_VALUE val2, PT_OP_TYPE cmp_op,
                 int val1_msgid, int val2_msgid, int error_type)
{
  invariant->val1 = val1;
  invariant->val2 = val2;
  invariant->cmp_op = cmp_op;
  invariant->val1_msgid = val1_msgid;
  invariant->val2_msgid = val2_msgid;
  invariant->error_type = error_type;
}

/*
 * check_serial_invariants() - check whether invariants have been violated
 *   return: Error code
 *   invariants(in):
 *   num_invariants(in):
 *   ret_msg_id(out):
 *
 * Note:
 */

static int
check_serial_invariants (SERIAL_INVARIANT * invariants, int num_invariants, int *ret_msg_id)
{
  int i, c;
  int error;
  DB_VALUE cmp_result;

  for (i = 0; i < num_invariants; i++)
    {

      error = numeric_db_value_compare (&invariants[i].val1, &invariants[i].val2, &cmp_result);
      if (error != NO_ERROR)
    {
      return error;
    }

      c = db_get_int (&cmp_result);
      switch (invariants[i].cmp_op)
    {
    case PT_GT:
      if (c > 0)
        {
          /* same as expected */
          continue;
        }
      break;
    case PT_GE:
      if (c >= 0)
        {
          continue;
        }
      break;
    case PT_LT:
      if (c < 0)
        {
          continue;
        }
      break;
    case PT_LE:
      if (c <= 0)
        {
          continue;
        }
      break;
    case PT_EQ:
      if (c == 0)
        {
          continue;
        }
      break;
    case PT_NE:
      if (c != 0)
        {
          continue;
        }
      break;
    default:
      /* impossible to get here! */
      assert (0);
      break;
    }

      /* get here means invariant violated! */
      if (invariants[i].val1_msgid != 0)
    {
      *ret_msg_id = invariants[i].val1_msgid;
      return invariants[i].error_type;
    }

      if (invariants[i].val2_msgid != 0)
    {
      *ret_msg_id = invariants[i].val2_msgid;
      return invariants[i].error_type;
    }

      /* impossible to get here! */
      assert (0);
    }

  return NO_ERROR;
}

/*
 * truncate_need_repl_log() - check whether truncate stmt need a replicate log
 *   return: true if the table has primary key,
 *           otherwise false
 *   statement(in):
 */
static bool
truncate_need_repl_log (PT_NODE * statement)
{
  PT_NODE *entity_spec = NULL;
  PT_NODE *entity_list = NULL;
  PT_NODE *entity = NULL;
  const char *class_name = NULL;
  MOP class_mop = NULL;
  SM_CLASS *class_ = NULL;
  SM_CLASS_CONSTRAINT *cons = NULL;
  int error = NO_ERROR;

  assert (statement != NULL);

  entity_spec = statement->info.truncate.spec;
  assert (entity_spec != NULL);

  entity_list = entity_spec->info.spec.flat_entity_list;
  assert (entity_list != NULL);

  for (entity = entity_list; entity != NULL; entity = entity->next)
    {
      class_name = entity->info.name.original;
      class_mop = db_find_class (class_name);
      if (class_mop == NULL)
    {
      return false;
    }

      error = au_fetch_class (class_mop, &class_, AU_FETCH_READ, DB_AUTH_NONE);
      if (error != NO_ERROR)
    {
      return false;
    }

      cons = classobj_find_cons_primary_key (class_->constraints);
      if (cons != NULL)
    {
      return true;
    }
    }

  return false;
}

/*
 * is_stmt_based_repl_type()
 *   return: true if it's a statement-based replication log statement
 *           otherwise false
 *   node(in):
 */
bool
is_stmt_based_repl_type (const PT_NODE * node)
{
  /* All DDLs will be replicated via schema replication */
  if (pt_is_ddl_statement (node))
    {
      return true;
    }

  /* some DMLs will also be replicated via schema replication instead of data replication */
  switch (node->node_type)
    {
    case PT_DROP_VARIABLE:
      return true;
    case PT_INSERT:
      if (node->info.insert.hint & PT_HINT_USE_SBR)
    {
      return true;
    }
      break;
    case PT_DELETE:
      if (node->info.delete_.hint & PT_HINT_USE_SBR)
    {
      return true;
    }
      break;
    case PT_UPDATE:
      if (node->info.update.hint & PT_HINT_USE_SBR)
    {
      return true;
    }
      break;
    default:
      break;
    }

  return false;
}

/*
 * do_evaluate_default_expr() - evaluates the default expressions, if any, for
 *              the attributes of a given class
 *   return: Error code
 *   parser(in):
 *   class_name(in):
 */
int
do_evaluate_default_expr (PARSER_CONTEXT * parser, PT_NODE * class_name)
{
  SM_ATTRIBUTE *att;
  SM_CLASS *smclass;
  int error;
  TP_DOMAIN_STATUS dom_status;
  char *user_name;
  DB_DATETIME *datetime;
  int month, day, year, hour, minute, second, millisecond;
  DB_VALUE default_value, format_val, lang_val;
  char *lang_str = NULL;
  int flag;
  TP_DOMAIN *result_domain = NULL;
  bool has_user_format;

  assert (class_name->node_type == PT_NAME);

  error = au_fetch_class_force (class_name->info.name.db_object, &smclass, AU_FETCH_READ);
  if (error != NO_ERROR)
    {
      return error;
    }

  for (att = smclass->attributes; att != NULL; att = (SM_ATTRIBUTE *) att->header.next)
    {
      if (att->default_value.default_expr.default_expr_type != DB_DEFAULT_NONE)
    {
      switch (att->default_value.default_expr.default_expr_type)
        {
        case DB_DEFAULT_SYSTIME:
          if (DB_IS_NULL (&parser->sys_datetime))
        {
          db_make_null (&default_value);
        }
          else
        {
          db_datetime_decode ((DB_DATETIME *) db_get_datetime (&parser->sys_datetime), &month, &day, &year,
                      &hour, &minute, &second, &millisecond);
          db_make_time (&default_value, hour, minute, second);
        }
          break;
        case DB_DEFAULT_CURRENTTIME:
          if (DB_IS_NULL (&parser->sys_datetime))
        {
          db_make_null (&default_value);
        }
          else
        {
          DB_TIME cur_time, db_time;
          const char *t_source, *t_dest;
          DB_DATETIME *datetime;

          datetime = db_get_datetime (&parser->sys_datetime);
          t_source = tz_get_system_timezone ();
          t_dest = tz_get_session_local_timezone ();
          db_time = datetime->time / 1000;
          error = tz_conv_tz_time_w_zone_name (&db_time, t_source, strlen (t_source), t_dest,
                               strlen (t_dest), &cur_time);
          db_value_put_encoded_time (&default_value, &cur_time);
        }
          break;
        case DB_DEFAULT_SYSDATE:
          if (DB_IS_NULL (&parser->sys_datetime))
        {
          db_make_null (&default_value);
        }
          else
        {
          datetime = db_get_datetime (&parser->sys_datetime);
          error = db_value_put_encoded_date (&default_value, &datetime->date);
        }
          break;
        case DB_DEFAULT_SYSDATETIME:
          error = pr_clone_value (&parser->sys_datetime, &default_value);
          break;
        case DB_DEFAULT_SYSTIMESTAMP:
          error = db_datetime_to_timestamp (&parser->sys_datetime, &default_value);
          break;
        case DB_DEFAULT_UNIX_TIMESTAMP:
          error = db_unix_timestamp (&parser->sys_datetime, &default_value);
          break;
        case DB_DEFAULT_USER:
          user_name = db_get_user_and_host_name ();
          error = db_make_string (&default_value, user_name);
          default_value.need_clear = true;
          break;
        case DB_DEFAULT_CURR_USER:
          user_name = db_get_user_name ();
          error = db_make_string (&default_value, user_name);
          default_value.need_clear = true;
          break;
        case DB_DEFAULT_CURRENTDATE:
        case DB_DEFAULT_CURRENTDATETIME:
          if (DB_IS_NULL (&parser->sys_datetime))
        {
          db_make_null (&default_value);
        }
          else
        {
          TZ_REGION system_tz_region, session_tz_region;
          DB_DATETIME dest_dt;
          DB_DATETIME *src_dt;

          src_dt = db_get_datetime (&parser->sys_datetime);
          tz_get_system_tz_region (&system_tz_region);
          tz_get_session_tz_region (&session_tz_region);
          error =
            tz_conv_tz_datetime_w_region (src_dt, &system_tz_region, &session_tz_region, &dest_dt, NULL, NULL);
          if (att->default_value.default_expr.default_expr_type == DB_DEFAULT_CURRENTDATE)
            {
              db_value_put_encoded_date (&default_value, &dest_dt.date);
            }
          else
            {
              db_make_datetime (&default_value, &dest_dt);
            }
        }
          break;
        case DB_DEFAULT_CURRENTTIMESTAMP:
          if (DB_IS_NULL (&parser->sys_datetime))
        {
          db_make_null (&default_value);
        }
          else
        {
          DB_DATE tmp_date;
          DB_TIME tmp_time;
          DB_TIMESTAMP tmp_timestamp;
          DB_DATETIME *sys_datetime;

          sys_datetime = db_get_datetime (&parser->sys_datetime);
          tmp_date = sys_datetime->date;
          tmp_time = sys_datetime->time / 1000;
          db_timestamp_encode_sys (&tmp_date, &tmp_time, &tmp_timestamp, NULL);
          db_make_timestamp (&default_value, tmp_timestamp);
        }
          break;
        default:
          break;
        }

      if (error != NO_ERROR)
        {
          break;
        }

      if (att->default_value.default_expr.default_expr_op == T_TO_CHAR)
        {
          if (att->default_value.default_expr.default_expr_format != NULL)
        {
          has_user_format = 1;
          db_make_string (&format_val, att->default_value.default_expr.default_expr_format);
        }
          else
        {
          has_user_format = 0;
          db_make_null (&format_val);
        }

          lang_str = prm_get_string_value (PRM_ID_INTL_DATE_LANG);
          lang_set_flag_from_lang (lang_str, has_user_format, 0, &flag);
          db_make_int (&lang_val, flag);

          if (!TP_IS_CHAR_TYPE (TP_DOMAIN_TYPE (att->domain)))
        {
          /* TO_CHAR returns a string value, we need to pass an expected domain of the result */
          if (TP_IS_CHAR_TYPE (DB_VALUE_TYPE (&default_value)))
            {
              result_domain = NULL;
            }
          else if (DB_IS_NULL (&format_val))
            {
              result_domain = tp_domain_resolve_default (DB_TYPE_STRING);
            }
          else
            {
              result_domain = tp_domain_resolve_value (&format_val, NULL);
            }
        }
          else
        {
          result_domain = att->domain;
        }

          error = db_to_char (&default_value, &format_val, &lang_val, &att->default_value.value, result_domain);

          if (has_user_format)
        {
          pr_clear_value (&format_val);
        }

          if (error != NO_ERROR)
        {
          break;
        }
        }
      else
        {
          pr_clone_value (&default_value, &att->default_value.value);
        }

      db_value_clear (&default_value);

      /* make sure the default value can be used for this attribute */
      dom_status = tp_value_cast (&att->default_value.value, &att->default_value.value, att->domain, false);
      if (dom_status != DOMAIN_COMPATIBLE)
        {
          error = tp_domain_status_er_set (dom_status, ARG_FILE_LINE, &att->default_value.value, att->domain);
          assert_release (error != NO_ERROR);

          break;
        }
    }
    }

  return error;
}

/*
 * do_create_serial_internal() -
 *   return: Error code
 *   serial_object(out):
 *   serial_name(in):
 *   current_val(in):
 *   inc_val(in):
 *   min_val(in):
 *   max_val(in):
 *   cyclic(in):
 *   started(in):
 *   comment(in):
 *   class_name(in):
 *   att_name(in):
 *
 * Note:
 */
static int
do_create_serial_internal (MOP * serial_object, const char *serial_name, DB_VALUE * start_val, DB_VALUE * inc_val,
               DB_VALUE * min_val, DB_VALUE * max_val, const int cyclic, const int cached_num,
               const int started, const char *comment, const char *class_name, const char *att_name)
{
  DB_OBJECT *ret_obj = NULL;
  DB_OTMPL *obj_tmpl = NULL;
  DB_VALUE value;
  DB_OBJECT *serial_class = NULL;
  char owner_name[DB_MAX_USER_LENGTH] = { '\0' };
  MOP owner = NULL;
  int au_save, error = NO_ERROR;

  db_make_null (&value);

  /* temporarily disable authorization to access _db_serial class */
  AU_DISABLE (au_save);

  serial_class = sm_find_class (CT_SERIAL_NAME);
  if (serial_class == NULL)
    {
      error = ER_QPROC_DB_SERIAL_NOT_FOUND;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      goto end;
    }

  obj_tmpl = dbt_create_object_internal ((MOP) serial_class);
  if (obj_tmpl == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }

  /* unique_name */
  db_make_string (&value, serial_name);
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_UNIQUE_NAME, &value);
  pr_clear_value (&value);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* name */
  db_make_string (&value, sm_remove_qualifier_name (serial_name));
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_NAME, &value);
  pr_clear_value (&value);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* owner */
  sm_qualifier_name (serial_name, owner_name, DB_MAX_USER_LENGTH);
  owner = owner_name[0] == '\0' ? Au_user : db_find_user (owner_name);

  if (!ws_is_same_object (owner, Au_user) && !au_is_dba_group_member (Au_user))
    {
      ERROR_SET_ERROR (error, ER_QPROC_CREATE_SERIAL_NOT_OWNER);
      goto end;
    }

  db_make_object (&value, owner);
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_OWNER, &value);
  pr_clear_value (&value);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* current_val */
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_CURRENT_VAL, start_val);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* increment_val */
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_INCREMENT_VAL, inc_val);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* min_val */
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_MIN_VAL, min_val);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* max_val */
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_MAX_VAL, max_val);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* start_val */
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_START_VAL, start_val);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* cyclic */
  db_make_int (&value, cyclic); /* always false */
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_CYCLIC, &value);
  pr_clear_value (&value);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* started */
  db_make_int (&value, started);
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_STARTED, &value);
  pr_clear_value (&value);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* comment */
  db_make_string (&value, comment);
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_COMMENT, &value);
  pr_clear_value (&value);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* class name */
  if (class_name)
    {
      db_make_string (&value, sm_remove_qualifier_name (class_name));
      error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_CLASS_NAME, &value);
      pr_clear_value (&value);
      if (error != NO_ERROR)
    {
      goto end;
    }
    }

  /* att name */
  if (att_name)
    {
      db_make_string (&value, att_name);
      error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_ATTR_NAME, &value);
      pr_clear_value (&value);
      if (error != NO_ERROR)
    {
      goto end;
    }
    }

  /* cached num */
  if (cached_num > 0)
    {
      db_make_int (&value, cached_num);
      error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_CACHED_NUM, &value);
      pr_clear_value (&value);
      if (error != NO_ERROR)
    {
      goto end;
    }
    }

  /* created_time && updated_time */
  error = db_set_otmpl_timestamps (obj_tmpl);
  if (error != NO_ERROR)
    {
      goto end;
    }

  ret_obj = dbt_finish_object (obj_tmpl);

  if (ret_obj == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
    }
  else if (serial_object != NULL)
    {
      *serial_object = ret_obj;
    }

end:
  if (obj_tmpl != NULL && ret_obj == NULL)
    {
      dbt_abort_object (obj_tmpl);
    }
  AU_ENABLE (au_save);
  return error;
}

/*
 * do_update_auto_increment_serial_on_rename() -
 *   return: Error code
 *   serial_obj(in/out):
 *   class_name(in):
 *   att_name(in):
 *
 * Note:
 */
int
do_update_auto_increment_serial_on_rename (MOP serial_obj, const char *class_name, const char *att_name)
{
  int error = NO_ERROR;
  DB_OBJECT *serial_object = NULL;
  DB_VALUE value;
  DB_OTMPL *obj_tmpl = NULL;
  char *serial_name = NULL;
  char att_downcase_name[SM_MAX_IDENTIFIER_LENGTH];
  size_t name_len;
  int save;
  bool au_disable_flag = false;

  if (!serial_obj || !class_name || !att_name)
    {
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  db_make_null (&value);

  serial_object = serial_obj;
  sm_downcase_name (att_name, att_downcase_name, SM_MAX_IDENTIFIER_LENGTH);
  att_name = att_downcase_name;

  /* serial_name : <class_name>_ai_<att_name> */
  name_len = (strlen (class_name) + strlen (att_name) + AUTO_INCREMENT_SERIAL_NAME_EXTRA_LENGTH + 1);
  serial_name = (char *) malloc (name_len);
  if (serial_name == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, name_len);
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }

  SET_AUTO_INCREMENT_SERIAL_NAME (serial_name, class_name, att_name);

  AU_DISABLE (save);
  au_disable_flag = true;

  /*
   * after serial.next_value, the currect value maybe changed, but cub_cas
   * still hold the old value. To get the new value. we need decache it
   * then refetch it from server again.
   */
  assert (WS_ISDIRTY (serial_object) == false);

  ws_decache (serial_object);
  /* no need to get last version for serial - actually, the purpose is AU_FETCH_WRITE, so fetch type is not relevant;
   * the last version will be locked and it will be considered visibile only if delid is not set */
  error = au_fetch_instance_force (serial_object, NULL, AU_FETCH_WRITE, LC_FETCH_MVCC_VERSION);
  if (error != NO_ERROR)
    {
      goto update_auto_increment_error;
    }

  obj_tmpl = dbt_edit_object (serial_object);
  if (obj_tmpl == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto update_auto_increment_error;
    }

  /* unique_name */
  db_make_string (&value, serial_name);
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_UNIQUE_NAME, &value);
  pr_clear_value (&value);
  if (error != NO_ERROR)
    {
      goto update_auto_increment_error;
    }

  /* name */
  db_make_string (&value, sm_remove_qualifier_name (serial_name));
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_NAME, &value);
  if (error != NO_ERROR)
    {
      goto update_auto_increment_error;
    }

  /* class name */
  pr_clear_value (&value);
  db_make_string (&value, sm_remove_qualifier_name (class_name));
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_CLASS_NAME, &value);
  pr_clear_value (&value);
  if (error != NO_ERROR)
    {
      goto update_auto_increment_error;
    }

  /* att name */
  db_make_string (&value, att_name);
  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_ATTR_NAME, &value);
  pr_clear_value (&value);
  if (error != NO_ERROR)
    {
      goto update_auto_increment_error;
    }

  /* updated_time */
  error = db_update_otmpl_timestamp (obj_tmpl);
  if (error != NO_ERROR)
    {
      goto update_auto_increment_error;
    }

  serial_object = dbt_finish_object (obj_tmpl);

  AU_ENABLE (save);
  au_disable_flag = false;

  if (serial_object == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto update_auto_increment_error;
    }

  free_and_init (serial_name);
  return NO_ERROR;

update_auto_increment_error:
  if (serial_name)
    {
      free_and_init (serial_name);
    }

  if (au_disable_flag == true)
    {
      AU_ENABLE (save);
    }

  /* if dbt_finish_object() succeeded, it would never come here, so we just check if obj_tmpl and clear it. */
  if (obj_tmpl != NULL)
    {
      dbt_abort_object (obj_tmpl);
    }

  return (error);
}

/*
 * do_reset_auto_increment_serial() -
 *   return: Error code
 *   serial_obj(in/out):
 */
int
do_reset_auto_increment_serial (MOP serial_obj)
{
  int error_code = NO_ERROR;
  DB_OBJECT *const serial_object = serial_obj;
  DB_OBJECT *edit_serial_object = NULL;
  DB_OTMPL *obj_tmpl = NULL;
  DB_VALUE start_value;
  DB_VALUE started_flag;

  if (serial_object == NULL)
    {
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  db_make_null (&start_value);
  db_make_null (&started_flag);

  error_code = db_get (serial_object, SERIAL_ATTR_MIN_VAL, &start_value);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  obj_tmpl = dbt_edit_object (serial_object);
  if (obj_tmpl == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error_code = er_errid ();
      goto error_exit;
    }

  error_code = dbt_put_internal (obj_tmpl, SERIAL_ATTR_CURRENT_VAL, &start_value);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  db_make_int (&started_flag, 0);
  error_code = dbt_put_internal (obj_tmpl, SERIAL_ATTR_STARTED, &started_flag);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  edit_serial_object = dbt_finish_object (obj_tmpl);
  if (edit_serial_object == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error_code = er_errid ();
      goto error_exit;
    }

  assert (edit_serial_object == serial_object);
  obj_tmpl = NULL;

  error_code = locator_flush_instance (edit_serial_object);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  db_value_clear (&start_value);
  db_value_clear (&started_flag);

  return error_code;

error_exit:

  /* We don't need to check return value of dbt_finish_object() since if it succeeded, it would never come here. */
  if (obj_tmpl != NULL)
    {
      dbt_abort_object (obj_tmpl);
    }

  db_value_clear (&start_value);
  db_value_clear (&started_flag);

  return error_code;
}


/*
 * do_change_auto_increment_serial() -
 *   return: Error code
 *   serial_obj(in/out):
 */
int
do_change_auto_increment_serial (PARSER_CONTEXT * const parser, MOP serial_obj, PT_NODE * node_new_val)
{
  int error_code = NO_ERROR;
  DB_OBJECT *const serial_object = serial_obj;
  DB_OBJECT *edit_serial_object = NULL;
  DB_OTMPL *obj_tmpl = NULL;

  DB_VALUE max_val;
  DB_VALUE started;
  DB_VALUE new_val;
  DB_VALUE cmp_result;
  DB_VALUE *pval = NULL;
  DB_DATA_STATUS data_status;


  int cmp;


  /*
   * 1. obtain NUMERIC value from node_new_val
   * 2. obtain max value of the serial.
   * 3. if the new value is greater than max, throw an error
   * 4. reset the serial: started = 0, cur = min = new cur;
   */


  if (serial_object == NULL)
    {
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  db_make_null (&max_val);
  db_make_null (&new_val);
  db_make_null (&started);
  db_make_int (&cmp_result, 0);


  /* create a NUMERIC value in new_val */
  db_value_domain_init (&new_val, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);
  pval = pt_value_to_db (parser, node_new_val);
  if (pval == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error_code = er_errid ();
      goto error_exit;
    }

  error_code = numeric_db_value_coerce_to_num (pval, &new_val, &data_status);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }


  /* get the serial's max from db. */
  error_code = db_get (serial_object, SERIAL_ATTR_MAX_VAL, &max_val);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }


  /* The new value must be lower than the max value */
  error_code = numeric_db_value_compare (&new_val, &max_val, &cmp_result);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  cmp = db_get_int (&cmp_result);
  if (cmp >= 0)
    {
      error_code = ER_AUTO_INCREMENT_NEWVAL_MUST_LT_MAXVAL;
      ERROR0 (error_code, ER_AUTO_INCREMENT_NEWVAL_MUST_LT_MAXVAL);
      goto error_exit;
    }

  /*
   * RESET serial: min = new_val; cur = new_val; started = 0 */

  obj_tmpl = dbt_edit_object (serial_object);
  if (obj_tmpl == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error_code = er_errid ();
      goto error_exit;
    }

  error_code = dbt_put_internal (obj_tmpl, SERIAL_ATTR_CURRENT_VAL, &new_val);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  error_code = dbt_put_internal (obj_tmpl, SERIAL_ATTR_MIN_VAL, &new_val);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  db_make_int (&started, 0);
  error_code = dbt_put_internal (obj_tmpl, SERIAL_ATTR_STARTED, &started);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  /* updated_time */
  error_code = db_update_otmpl_timestamp (obj_tmpl);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  edit_serial_object = dbt_finish_object (obj_tmpl);
  if (edit_serial_object == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error_code = er_errid ();
      goto error_exit;
    }

  assert (edit_serial_object == serial_object);
  obj_tmpl = NULL;

  error_code = locator_flush_instance (edit_serial_object);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  goto normal_exit;


error_exit:

  if (obj_tmpl != NULL)
    {
      dbt_abort_object (obj_tmpl);
    }

normal_exit:

  db_value_clear (&max_val);
  db_value_clear (&new_val);
  db_value_clear (&started);
  db_value_clear (&cmp_result);

  return error_code;
}


static MOP
do_get_obj_id (DB_IDENTIFIER * obj_id, DB_OBJECT * class_mop, const char *name, const char *attr_name)
{
  DB_OBJECT *mop;
  DB_VALUE val;
  DB_IDENTIFIER *db_id;
  char *p;
  size_t name_size;
  int save;

  OID_SET_NULL (obj_id);

  if (class_mop == NULL || name == NULL)
    {
      return NULL;
    }

  name_size = intl_identifier_lower_string_size (name);
  p = (char *) malloc (name_size + 1);
  if (p == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (name_size + 1));
      return NULL;
    }

  intl_identifier_lower (name, p);
  db_make_string (&val, p);

  AU_DISABLE (save);
  mop = db_find_unique (class_mop, attr_name, &val);
  AU_ENABLE (save);

  if (mop == NULL)
    {
      assert (er_errid () != NO_ERROR);

      if (er_errid () == ER_OBJ_OBJECT_NOT_FOUND)
    {
      er_clear ();
    }
    }
  else
    {
      db_id = ws_identifier (mop);

      if (db_id != NULL)
    {
      *obj_id = *db_id;
    }
      else
    {
      mop = NULL;
    }
    }

  pr_clear_value (&val);
  free_and_init (p);

  return mop;
}


/*
 * do_get_serial_obj_id() -
 *   return: serial object
 *   serial_obj_id(out):
 *   serial_class_mop(in):
 *   serial_name(in):
 *
 * Note:
 */
MOP
do_get_serial_obj_id (DB_IDENTIFIER * serial_obj_id, DB_OBJECT * serial_class_mop, const char *serial_name)
{
  MOP serial_mop = NULL;

  serial_mop = do_get_obj_id (serial_obj_id, serial_class_mop, serial_name, SERIAL_ATTR_UNIQUE_NAME);
  if (serial_mop)
    {
      return serial_mop;
    }

  /* 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)
    {
      char other_serial_name[DB_MAX_SERIAL_NAME_LENGTH] = { '\0' };

      do_find_serial_by_query (serial_name, other_serial_name, DB_MAX_SERIAL_NAME_LENGTH);
      if (other_serial_name[0] != '\0')
    {
      if (db_get_client_statement_type () == CUBRID_STMT_CREATE_SERIAL)
        {
          /* maybe unloaded from version 11.2+ or later */
          db_set_client_type (DB_CLIENT_TYPE_ADMIN_LOADDB_COMPAT_UNDER_11_4);
          return NULL;
        }

      serial_mop = do_get_obj_id (serial_obj_id, serial_class_mop, other_serial_name, SERIAL_ATTR_UNIQUE_NAME);
      if (serial_mop)
        {
          return serial_mop;
        }
    }
    }

  return NULL;
}

/*
 * do_get_serial_cached_num() -
 *   return: Error code
 *   cached_num(out) :
 *   serial_obj(in)  :
 *
 * Note:
 */
int
do_get_serial_cached_num (int *cached_num, MOP serial_obj)
{
  DB_VALUE cached_num_val;
  int error;

  error = db_get (serial_obj, SERIAL_ATTR_CACHED_NUM, &cached_num_val);
  if (error != NO_ERROR)
    {
      return error;
    }

  assert (DB_VALUE_TYPE (&cached_num_val) == DB_TYPE_INTEGER);

  *cached_num = db_get_int (&cached_num_val);

  return NO_ERROR;
}

/*
 * do_create_serial() -
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in):
 *
 * Note:
 */
int
do_create_serial (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  DB_OBJECT *serial_class = NULL, *serial_object = NULL;
  MOP serial_mop;
  DB_IDENTIFIER serial_obj_id;
  DB_VALUE value, *pval = NULL;

  const char *serial_name = NULL;
  char downcase_serial_name[DB_MAX_IDENTIFIER_LENGTH] = { '\0' };
  PT_NODE *start_val_node;
  PT_NODE *inc_val_node;
  PT_NODE *max_val_node;
  PT_NODE *min_val_node;
  PT_NODE *cached_num_node;

  DB_VALUE zero, e38, negative_e38;
  DB_VALUE start_val, inc_val, max_val, min_val, cached_num_val;
  DB_VALUE cmp_result;
  DB_VALUE tmp_val;
  DB_VALUE abs_inc_val, range_val;

  int min_val_msgid = 0;
  int max_val_msgid = 0;
  int start_val_msgid = 0;
  int inc_val_msgid = 0;
  int ret_msg_id = 0;
  SERIAL_INVARIANT invariants[MAX_SERIAL_INVARIANT];
  int ninvars = 0;
  int inc_val_flag = 0, cyclic;
  int cached_num;
  DB_DATA_STATUS data_stat;
  int error = NO_ERROR;
  int save;
  bool au_disable_flag = false;
  size_t name_size;
  const char *comment = NULL;

  CHECK_MODIFICATION_ERROR ();

  db_make_null (&value);
  db_make_null (&zero);
  db_make_null (&e38);
  db_make_null (&negative_e38);
  db_make_null (&start_val);
  db_make_null (&inc_val);
  db_make_null (&max_val);
  db_make_null (&min_val);
  db_make_null (&abs_inc_val);
  db_make_null (&range_val);

  /*
   * find _db_serial class
   */
  serial_class = sm_find_class (CT_SERIAL_NAME);
  if (serial_class == NULL)
    {
      error = ER_QPROC_DB_SERIAL_NOT_FOUND;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      goto end;
    }

  /*
   * lookup if serial object name already exists?
   */
  serial_name = PT_NODE_SR_NAME (statement);
  sm_downcase_name (serial_name, downcase_serial_name, DB_MAX_IDENTIFIER_LENGTH);
  serial_mop = do_get_serial_obj_id (&serial_obj_id, serial_class, downcase_serial_name);
  if (serial_mop != NULL)
    {
      error = ER_QPROC_SERIAL_ALREADY_EXIST;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, serial_name);
      PT_ERRORmf (parser, statement, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SERIAL_ALREADY_EXIST, serial_name);
      goto end;
    }

  /* get all values as string */
  numeric_coerce_string_to_num ("0", 1, INTL_CODESET_ISO88591, &zero);
  numeric_coerce_string_to_num (DB_SERIAL_MAX, strlen (DB_SERIAL_MAX), INTL_CODESET_ISO88591, &e38);
  numeric_coerce_string_to_num (DB_SERIAL_MIN, strlen (DB_SERIAL_MIN), INTL_CODESET_ISO88591, &negative_e38);
  db_make_int (&cmp_result, 0);

  start_val_node = PT_NODE_SR_START_VAL (statement);
  inc_val_node = PT_NODE_SR_INCREMENT_VAL (statement);
  min_val_node = PT_NODE_SR_MIN_VAL (statement);
  max_val_node = PT_NODE_SR_MAX_VAL (statement);

  /* increment_val */
  db_value_domain_init (&inc_val, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);
  if (inc_val_node != NULL)
    {
      pval = pt_value_to_db (parser, inc_val_node);
      if (pval == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }

      error = numeric_db_value_coerce_to_num (pval, &inc_val, &data_stat);
      if (error != NO_ERROR)
    {
      goto end;
    }
      pval = NULL;

      /* check if increment value is 0 */
      error = numeric_db_value_compare (&inc_val, &zero, &cmp_result);
      if (error != NO_ERROR)
    {
      goto end;
    }

      inc_val_flag = db_get_int (&cmp_result);
      if (inc_val_flag == 0)
    {
      error = ER_INVALID_SERIAL_VALUE;
      PT_ERRORmf (parser, statement, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SERIAL_INC_VAL_ZERO, 0);
      goto end;
    }
      inc_val_msgid = MSGCAT_SEMANTIC_SERIAL_INC_VAL_INVALID;
    }
  else
    {
      /* inc_val = 1; */
      db_make_int (&value, 1);
      error = numeric_db_value_coerce_to_num (&value, &inc_val, &data_stat);
      if (error != NO_ERROR)
    {
      goto end;
    }

      inc_val_flag = +1;
    }

  /* start_val 1 */
  db_value_domain_init (&start_val, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);
  if (start_val_node != NULL)
    {
      pval = pt_value_to_db (parser, start_val_node);
      if (pval == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }
      error = numeric_db_value_coerce_to_num (pval, &start_val, &data_stat);
      if (error != NO_ERROR)
    {
      goto end;
    }
      pval = NULL;
      start_val_msgid = MSGCAT_SEMANTIC_SERIAL_START_VAL_INVALID;
    }

  db_value_domain_init (&min_val, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);
  /*
   * min_val comes from several sources, it can be one of them:
   * 1. user input
   * 2. start_val
   * 3. 1
   * 4. -e36
   * min_val_msgid is the proper message id. it's for error message generation
   * when min_val violates some invariants.
   * if min_val is 1 or -e36, min_val_msgid is set to 0 (default value) because
   *  constants can't be the reason which violate invariants.
   * if min_val is from user input, min_val_msgid is set to
   * MSGCAT_SEMANTIC_SERIAL_MIN_VAL_INVALID.
   * if min_val is from start_val, min_val_msgid is set to
   * MSGCAT_SEMANTIC_SERIAL_START_VAL_INVALID.
   */
  if (min_val_node != NULL)
    {
      pval = pt_value_to_db (parser, min_val_node);
      if (pval == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }

      error = numeric_db_value_coerce_to_num (pval, &min_val, &data_stat);
      if (error != NO_ERROR)
    {
      goto end;
    }
      pval = NULL;
      min_val_msgid = MSGCAT_SEMANTIC_SERIAL_MIN_VAL_INVALID;
    }
  else
    {
      if (inc_val_flag > 0)
    {
      if (start_val_node != NULL)
        {
          db_value_clone (&start_val, &min_val);
          min_val_msgid = MSGCAT_SEMANTIC_SERIAL_START_VAL_INVALID;
        }
      else
        {
          /* min_val = 1; */
          db_make_int (&value, 1);
          error = numeric_db_value_coerce_to_num (&value, &min_val, &data_stat);
          if (error != NO_ERROR)
        {
          goto end;
        }
        }
    }
      else
    {
      /* min_val */
      db_value_clone (&negative_e38, &min_val);
    }
    }

  /* max_val */
  db_value_domain_init (&max_val, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);

  if (max_val_node != NULL)
    {
      pval = pt_value_to_db (parser, max_val_node);
      if (pval == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }

      error = numeric_db_value_coerce_to_num (pval, &max_val, &data_stat);
      if (error != NO_ERROR)
    {
      goto end;
    }
      pval = NULL;
      max_val_msgid = MSGCAT_SEMANTIC_SERIAL_MAX_VAL_INVALID;
    }
  else
    {
      if (inc_val_flag > 0)
    {
      /* max_val */
      db_value_clone (&e38, &max_val);
    }
      else
    {
      if (start_val_node != NULL)
        {
          /* max_val = start_val */
          db_value_clone (&start_val, &max_val);
          max_val_msgid = MSGCAT_SEMANTIC_SERIAL_START_VAL_INVALID;
        }
      else
        {
          /* max_val = -1; */
          db_make_int (&value, -1);
          error = numeric_db_value_coerce_to_num (&value, &max_val, &data_stat);
          if (error != NO_ERROR)
        {
          goto end;
        }
        }
    }
    }

  /* start_val 2 */
  if (start_val_node == NULL)
    {
      pr_clear_value (&start_val);
      if (inc_val_flag > 0)
    {
      /* start_val = min_val; */
      db_value_clone (&min_val, &start_val);
      start_val_msgid = MSGCAT_SEMANTIC_SERIAL_MAX_VAL_INVALID;
    }
      else
    {
      /* start_val = max_val; */
      db_value_clone (&max_val, &start_val);
      start_val_msgid = MSGCAT_SEMANTIC_SERIAL_MIN_VAL_INVALID;
    }
    }

  /* cyclic */
  cyclic = PT_NODE_SR_CYCLIC (statement);

  /*
   * check values
   * min_val    start_val     max_val
   *    |--^--^--^--o--^--^--^--^---|
   *                   <--> inc_val
   */

  /*
   * the following invariants must hold:
   * min_val >= negative_e38
   * max_val <= e38
   * min_val < max_val
   * min_val <= start_val
   * max_val >= start_val
   * inc_val != zero
   * abs(inc_val) <= (max_val - min_val)
   */

  /*
   * invariant for min_val >= negative_e38.
   * if min_val_msgid == MSGCAT_SEMANTIC_SERIAL_START_VAL_INVALID,
   * that means the value of min_val is from start_val, if the invariant
   * is violated, start_val invalid error message should be displayed
   * instead of min_val underflow. the val2_msgid is 0 because negative_e38
   * cannot be the reason which violates the invariant.
   */
  initialize_serial_invariant (&invariants[ninvars++], min_val, negative_e38, PT_GE,
                   ((min_val_msgid == MSGCAT_SEMANTIC_SERIAL_START_VAL_INVALID)
                ? MSGCAT_SEMANTIC_SERIAL_START_VAL_INVALID : MSGCAT_SEMANTIC_SERIAL_MIN_VAL_UNDERFLOW),
                   0, ER_QPROC_SERIAL_RANGE_OVERFLOW);

  /*
   * invariant for max_val <= e38. Like the above invariant, if
   * max_val_msgid == MSGCAT_SEMANTIC_SERIAL_START_VAL_INVALID,
   * start_val invalid error message should be displayed if the invariant
   * is violated.
   */
  initialize_serial_invariant (&invariants[ninvars++], max_val, e38, PT_LE,
                   ((max_val_msgid == MSGCAT_SEMANTIC_SERIAL_START_VAL_INVALID)
                ? MSGCAT_SEMANTIC_SERIAL_START_VAL_INVALID : MSGCAT_SEMANTIC_SERIAL_MAX_VAL_OVERFLOW),
                   0, ER_QPROC_SERIAL_RANGE_OVERFLOW);

  /* invariant for min_val < max_val. */
  initialize_serial_invariant (&invariants[ninvars++], min_val, max_val, PT_LT, min_val_msgid, max_val_msgid,
                   ER_INVALID_SERIAL_VALUE);

  /* invariant for min_val <= start_val */
  initialize_serial_invariant (&invariants[ninvars++], min_val, start_val, PT_LE, min_val_msgid, start_val_msgid,
                   ER_INVALID_SERIAL_VALUE);

  /* invariant for max_val >= start_val */
  initialize_serial_invariant (&invariants[ninvars++], max_val, start_val, PT_GE, max_val_msgid, start_val_msgid,
                   ER_INVALID_SERIAL_VALUE);

  /* invariant for inc_val != zero */
  initialize_serial_invariant (&invariants[ninvars++], inc_val, zero, PT_NE, MSGCAT_SEMANTIC_SERIAL_INC_VAL_ZERO, 0,
                   ER_INVALID_SERIAL_VALUE);

  /*
   * invariant for abs(inc_val) <= (max_val - min_val).
   * if this invariant is violated, inc_val, min_val or max_val should be
   * responsible for it. If max_val_msgid == 0, which means max_val is
   * initialized from a constant, not inputted by user,  in this case, we don't
   * expect max_val should be responsible for the violation.
   */
  error = numeric_db_value_sub (&max_val, &min_val, &range_val);
  if (error == ER_IT_DATA_OVERFLOW)
    {
      // max - min might be flooded. Regard the range is big enough.
      numeric_coerce_string_to_num (DB_SERIAL_MAX, strlen (DB_SERIAL_MAX), INTL_CODESET_ISO88591, &range_val);
      er_clear ();
    }

  db_abs_dbval (&abs_inc_val, &inc_val);
  initialize_serial_invariant (&invariants[ninvars++], abs_inc_val, range_val, PT_LE, inc_val_msgid,
                   (max_val_msgid == 0) ? min_val_msgid : max_val_msgid, ER_INVALID_SERIAL_VALUE);

  /* cached num */
  cached_num_node = PT_NODE_SR_CACHED_NUM_VAL (statement);
  if (cached_num_node != NULL)
    {
      DB_VALUE cached_num_int_val, abs_cached_range_val;
      assert (cached_num_node->type_enum == PT_TYPE_INTEGER);

      cached_num = cached_num_node->info.value.data_value.i;

      /* ABS (cache_num * inc_val) <= range_val */

      db_make_int (&cached_num_int_val, cached_num);
      db_value_domain_init (&cached_num_val, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);
      error = numeric_db_value_coerce_to_num (&cached_num_int_val, &cached_num_val, &data_stat);
      if (error != NO_ERROR)
    {
      goto end;
    }

      /* ABS (cache_num * inc_val) */
      error = numeric_db_value_mul (&inc_val, &cached_num_val, &tmp_val);
      if (error != NO_ERROR)
    {
      goto end;
    }

      error = db_abs_dbval (&abs_cached_range_val, &tmp_val);
      if (error != NO_ERROR)
    {
      goto end;
    }

      initialize_serial_invariant (&invariants[ninvars++], abs_cached_range_val, range_val, PT_LE,
                   MSGCAT_SEMANTIC_SERIAL_CACHED_NUM_INVALID_RANGE, 0, ER_INVALID_SERIAL_VALUE);

      pr_clear_value (&tmp_val);
      pr_clear_value (&abs_cached_range_val);
    }
  else
    {
      cached_num = 0;
    }

  assert (ninvars <= MAX_SERIAL_INVARIANT);
  error = check_serial_invariants (invariants, ninvars, &ret_msg_id);

  if (error != NO_ERROR)
    {
      if (error == ER_QPROC_SERIAL_RANGE_OVERFLOW || error == ER_INVALID_SERIAL_VALUE)
    {
      PT_ERRORmf (parser, statement, MSGCAT_SET_PARSER_SEMANTIC, ret_msg_id, 0);
    }
      goto end;
    }

  /* comment */
  if (statement->info.serial.comment != NULL)
    {
      assert (statement->info.serial.comment->node_type == PT_VALUE);
      comment = (char *) PT_VALUE_GET_BYTES (statement->info.serial.comment);
      if (comment == NULL)
    {
      error = (er_errid () != NO_ERROR) ? er_errid () : ER_FAILED;
      goto end;
    }
    }

  /* now create serial object which is insert into _db_serial */
  AU_DISABLE (save);
  au_disable_flag = true;

  error = do_create_serial_internal (&serial_object, downcase_serial_name, &start_val, &inc_val, &min_val,
                     &max_val, cyclic, cached_num, 0, comment, NULL, NULL);

  AU_ENABLE (save);
  au_disable_flag = false;

  if (error < 0)
    {
      goto end;
    }

  return NO_ERROR;

end:
  if (au_disable_flag == true)
    {
      AU_ENABLE (save);
    }

  return error;
}

/*
 * do_create_auto_increment_serial() -
 *   return: Error code
 *   parser(in): Parser context
 *   serial_object(out):
 *   class_name(in):
 *   att(in):
 *
 * Note:
 */
int
do_create_auto_increment_serial (PARSER_CONTEXT * parser, MOP * serial_object, const char *class_name, PT_NODE * att)
{
  MOP serial_class = NULL, serial_mop;
  DB_IDENTIFIER serial_obj_id;
  DB_DATA_STATUS data_stat;
  int error = NO_ERROR;
  PT_NODE *auto_increment_node, *start_val_node, *inc_val_node;
  PT_NODE *dtyp;
  char *att_name = NULL, *serial_name = NULL;
  DB_VALUE start_val, inc_val, max_val, min_val;
  DB_VALUE zero, value, *pval = NULL;
  DB_VALUE cmp_result;
  int i;
  DB_VALUE e38;
  char *p, num[DB_MAX_NUMERIC_PRECISION + 1];
  char att_downcase_name[SM_MAX_IDENTIFIER_LENGTH];
  size_t name_len;

  db_make_null (&e38);
  db_make_null (&value);
  db_make_null (&zero);
  db_make_null (&cmp_result);
  db_make_null (&start_val);
  db_make_null (&inc_val);
  db_make_null (&max_val);
  db_make_null (&min_val);

  numeric_coerce_string_to_num ("0", 1, INTL_CODESET_ISO88591, &zero);
  numeric_coerce_string_to_num (DB_SERIAL_MAX, DB_MAX_NUMERIC_PRECISION, INTL_CODESET_ISO88591, &e38);

  assert_release (att->info.attr_def.auto_increment != NULL);
  auto_increment_node = att->info.attr_def.auto_increment;
  if (auto_increment_node == NULL)
    {
      goto end;
    }

  /*
   * find _db_serial
   */
  serial_class = sm_find_class (CT_SERIAL_NAME);
  if (serial_class == NULL)
    {
      error = ER_QPROC_DB_SERIAL_NOT_FOUND;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      goto end;
    }

  att_name = (char *) (att->info.attr_def.attr_name->alias_print
               ? att->info.attr_def.attr_name->alias_print : att->info.attr_def.attr_name->info.name.original);

  sm_downcase_name (att_name, att_downcase_name, SM_MAX_IDENTIFIER_LENGTH);
  att_name = att_downcase_name;

  /* serial_name : <class_name>_ai_<att_name> */
  name_len = (strlen (class_name) + strlen (att_name) + AUTO_INCREMENT_SERIAL_NAME_EXTRA_LENGTH + 1);
  serial_name = (char *) malloc (name_len);
  if (serial_name == NULL)
    {
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, name_len);
      goto end;
    }

  SET_AUTO_INCREMENT_SERIAL_NAME (serial_name, class_name, att_name);

  serial_mop = do_get_serial_obj_id (&serial_obj_id, serial_class, serial_name);
  if (serial_mop != NULL)
    {
      error = ER_AUTO_INCREMENT_SERIAL_ALREADY_EXIST;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      goto end;
    }

  error = er_errid ();
  if (error != NO_ERROR)
    {
      goto end;
    }

  start_val_node = auto_increment_node->info.auto_increment.start_val;
  inc_val_node = auto_increment_node->info.auto_increment.increment_val;

  /* increment_val */
  db_value_domain_init (&inc_val, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);

  if (inc_val_node != NULL)
    {
      pval = pt_value_to_db (parser, inc_val_node);
      if (pval == NULL)
    {
      error = ER_INVALID_SERIAL_VALUE;
      goto end;
    }

      error = numeric_db_value_coerce_to_num (pval, &inc_val, &data_stat);
      if (error != NO_ERROR)
    {
      goto end;
    }

      pval = NULL;

      /* check increment value */
      error = numeric_db_value_compare (&inc_val, &zero, &cmp_result);
      if (error != NO_ERROR)
    {
      goto end;
    }

      if (db_get_int (&cmp_result) <= 0)
    {
      error = ER_INCREMENT_VALUE_CANNOT_BE_ZERO;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      goto end;
    }
    }
  else
    {
      /* set to 1 */
      db_make_int (&value, 1);
      error = numeric_db_value_coerce_to_num (&value, &inc_val, &data_stat);
      if (error != NO_ERROR)
    {
      goto end;
    }
    }

  /* start_val */
  db_value_domain_init (&start_val, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);
  if (start_val_node != NULL)
    {
      pval = pt_value_to_db (parser, start_val_node);
      if (pval == NULL)
    {
      error = ER_INVALID_SERIAL_VALUE;
      goto end;
    }
      error = numeric_db_value_coerce_to_num (pval, &start_val, &data_stat);
      if (error != NO_ERROR)
    {
      goto end;
    }
      pval = NULL;
    }
  else
    {
      /* set to 1 */
      db_make_int (&value, 1);
      error = numeric_db_value_coerce_to_num (&value, &start_val, &data_stat);
      if (error != NO_ERROR)
    {
      goto end;
    }
    }

  /* min value = start_val */
  db_value_clone (&start_val, &min_val);

  /* max value - depends on att's domain */
  db_value_domain_init (&max_val, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);

  dtyp = att->data_type;
  switch (att->type_enum)
    {
    case PT_TYPE_INTEGER:
      db_make_int (&value, DB_INT32_MAX);
      break;
    case PT_TYPE_BIGINT:
      db_make_bigint (&value, DB_BIGINT_MAX);
      break;
    case PT_TYPE_SMALLINT:
      db_make_int (&value, DB_INT16_MAX);
      break;
    case PT_TYPE_NUMERIC:
      memset (num, '\0', DB_MAX_NUMERIC_PRECISION + 1);
      for (i = 0, p = num; i < dtyp->info.data_type.precision; i++, p++)
    {
      *p = '9';
    }

      *p = '\0';

      (void) numeric_coerce_string_to_num (num, dtyp->info.data_type.precision, INTL_CODESET_ISO88591, &value);
      break;
    default:
      /* max numeric */
      db_value_clone (&e38, &value);
    }

  error = numeric_db_value_coerce_to_num (&value, &max_val, &data_stat);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* check (start_val < max_val) */
  if (tp_value_compare (&start_val, &max_val, 1, 0) != DB_LT)
    {
      error = ER_AUTO_INCREMENT_STARTVAL_MUST_LT_MAXVAL;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      goto end;
    }

  /* create auto increment serial object */
  error =
    do_create_serial_internal (serial_object, serial_name, &start_val, &inc_val, &min_val, &max_val, 0, 0, 0, NULL,
                   class_name, att_name);
  if (error < 0)
    {
      goto end;
    }

  pr_clear_value (&e38);
  pr_clear_value (&value);
  pr_clear_value (&zero);
  pr_clear_value (&cmp_result);
  pr_clear_value (&start_val);
  pr_clear_value (&inc_val);
  pr_clear_value (&max_val);
  pr_clear_value (&min_val);

  free_and_init (serial_name);

  return NO_ERROR;

end:
  pr_clear_value (&e38);
  pr_clear_value (&value);
  pr_clear_value (&zero);
  pr_clear_value (&cmp_result);
  pr_clear_value (&start_val);
  pr_clear_value (&inc_val);
  pr_clear_value (&max_val);
  pr_clear_value (&min_val);

  if (serial_name)
    {
      free_and_init (serial_name);
    }

  return error;
}

/*
 * do_update_maxvalue_of_auto_increment_serial()
 *   usage: update max_val of serial object
 *   return: Error code
 *   parser(in): Parser context
 *   serial_object(out):
 *   class_name(in):
 *   att(in):
 *
 * Note:
 */
int
do_update_maxvalue_of_auto_increment_serial (PARSER_CONTEXT * parser, MOP * serial_object, const char *class_name,
                         PT_NODE * att)
{
  MOP serial_class, serial_mop;
  DB_OTMPL *obj_tmpl = NULL;
  DB_IDENTIFIER serial_obj_id;
  DB_DATA_STATUS data_stat;
  int error = NO_ERROR;
  PT_NODE *dtyp;
  char *att_name = NULL, *serial_name = NULL;
  DB_VALUE e38, current_val, max_val, value;
  int i, compare_result, save;
  char *p, num[DB_MAX_NUMERIC_PRECISION + 1];
  char att_downcase_name[SM_MAX_IDENTIFIER_LENGTH];
  size_t name_len;
  bool au_disable_flag = false;

  db_make_null (&e38);
  db_make_null (&value);
  db_make_null (&current_val);
  db_make_null (&max_val);
  OID_SET_NULL (&serial_obj_id);

  numeric_coerce_string_to_num (DB_SERIAL_MAX, strlen (DB_SERIAL_MAX), INTL_CODESET_ISO88591, &e38);

  assert (serial_object != NULL);

  /* find _db_serial */
  serial_class = sm_find_class (CT_SERIAL_NAME);
  if (serial_class == NULL)
    {
      error = ER_QPROC_DB_SERIAL_NOT_FOUND;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      goto end;
    }

  att_name = (char *) (att->info.attr_def.attr_name->alias_print
               ? att->info.attr_def.attr_name->alias_print : att->info.attr_def.attr_name->info.name.original);

  sm_downcase_name (att_name, att_downcase_name, SM_MAX_IDENTIFIER_LENGTH);
  att_name = att_downcase_name;

  /* serial_name : <class_name>_ai_<att_name> */
  name_len = (strlen (class_name) + strlen (att_name) + AUTO_INCREMENT_SERIAL_NAME_EXTRA_LENGTH + 1);
  serial_name = (char *) malloc (name_len);
  if (serial_name == NULL)
    {
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, name_len);
      goto end;
    }

  SET_AUTO_INCREMENT_SERIAL_NAME (serial_name, class_name, att_name);

  /* get serial mop by serial name */
  serial_mop = do_get_serial_obj_id (&serial_obj_id, serial_class, serial_name);
  if (serial_mop == NULL)
    {
      error = ER_QPROC_SERIAL_NOT_FOUND;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, serial_name);
      goto end;
    }

  /*
   * after serial.next_value, the currect value maybe changed, but cub_cas
   * still hold the old value. To get the new value. we need decache it
   * then refetch it from server again.
   */
  assert (WS_ISDIRTY (serial_mop) == false);

  ws_decache (serial_mop);

  /* no need to get the last version for serial - actually, AU_FETCH_WRITE will get only last version, for locking */
  error = au_fetch_instance_force (serial_mop, NULL, AU_FETCH_WRITE, LC_FETCH_MVCC_VERSION);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* get current value */
  error = db_get (serial_mop, SERIAL_ATTR_CURRENT_VAL, &current_val);
  if (error < 0)
    {
      goto end;
    }

  /* max value - depends on att's domain */
  db_value_domain_init (&max_val, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);

  dtyp = att->data_type;
  switch (att->type_enum)
    {
    case PT_TYPE_INTEGER:
      db_make_int (&value, DB_INT32_MAX);
      break;
    case PT_TYPE_BIGINT:
      db_make_bigint (&value, DB_BIGINT_MAX);
      break;
    case PT_TYPE_SMALLINT:
      db_make_int (&value, DB_INT16_MAX);
      break;
    case PT_TYPE_NUMERIC:
      memset (num, '\0', DB_MAX_NUMERIC_PRECISION + 1);
      for (i = 0, p = num; i < dtyp->info.data_type.precision; i++, p++)
    {
      *p = '9';
    }

      *p = '\0';

      (void) numeric_coerce_string_to_num (num, dtyp->info.data_type.precision, INTL_CODESET_ISO88591, &value);
      break;
    default:
      /* max numeric */
      db_value_clone (&e38, &value);
    }

  error = numeric_db_value_coerce_to_num (&value, &max_val, &data_stat);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* check (current_val <= max_val) */
  compare_result = tp_value_compare (&current_val, &max_val, 1, 0);
  if (compare_result != DB_LT && compare_result != DB_EQ)
    {
      error = ER_AUTO_INCREMENT_STARTVAL_MUST_LT_MAXVAL;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      goto end;
    }

  /* update serial object in _db_serial */
  AU_DISABLE (save);
  au_disable_flag = true;

  obj_tmpl = dbt_edit_object (serial_mop);
  if (obj_tmpl == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }

  error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_MAX_VAL, &max_val);
  if (error < 0)
    {
      goto end;
    }

  serial_mop = dbt_finish_object (obj_tmpl);
  if (serial_mop == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }
  else
    {
      *serial_object = serial_mop;
      /* obj_tmpl has been released by dbt_finish_object() */
      obj_tmpl = NULL;
    }

end:
  if (!OID_ISNULL (&serial_obj_id))
    {
      (void) serial_decache ((OID *) (&serial_obj_id));
    }

  if (au_disable_flag == true)
    {
      AU_ENABLE (save);
    }

  pr_clear_value (&e38);
  pr_clear_value (&value);
  pr_clear_value (&current_val);
  pr_clear_value (&max_val);

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

  if (obj_tmpl != NULL)
    {
      dbt_abort_object (obj_tmpl);
    }

  return error;
}

/*
 * do_alter_serial() -
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in):
 *
 * Note:
 */
int
do_alter_serial (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  DB_OBJECT *serial_class = NULL, *serial_object = NULL;
  DB_IDENTIFIER serial_obj_id;
  DB_OTMPL *obj_tmpl = NULL;
  DB_VALUE value, *pval;

  char *name = NULL;
  PT_NODE *start_val_node;
  PT_NODE *inc_val_node;
  PT_NODE *max_val_node;
  PT_NODE *min_val_node;
  PT_NODE *cached_num_node;

  DB_VALUE zero, e38, negative_e38;
  DB_DATA_STATUS data_stat;
  DB_VALUE old_inc_val, old_max_val, old_min_val, old_cached_num;
  DB_VALUE current_val, start_val, cached_num_val;
  DB_VALUE new_inc_val, new_max_val, new_min_val;
  DB_VALUE cmp_result;
  DB_VALUE class_name_val;
  DB_VALUE tmp_val;
  DB_VALUE abs_inc_val, range_val;
  int cached_num;
  int ret_msg_id = 0;

  const char *serial_name = NULL, *serial_owner_name = NULL;
  char user_specified_serial_name[DB_MAX_SERIAL_NAME_LENGTH] = { '\0' };
  MOP serial_mop = NULL, owner_mop = NULL;
  const char *comment = NULL;

  const PT_ALTER_CODE alter_serial_code = statement->info.serial.code;

  int new_inc_val_flag = 0, new_cyclic;
  bool cur_val_change, inc_val_change, max_val_change, min_val_change, cyclic_change, cached_num_change;

  int error = NO_ERROR;
  int save;

  SERIAL_INVARIANT invariants[MAX_SERIAL_INVARIANT];
  int ninvars = 0;

  CHECK_MODIFICATION_ERROR ();

  db_make_null (&value);
  db_make_null (&zero);
  db_make_null (&e38);
  db_make_null (&negative_e38);
  db_make_null (&old_inc_val);
  db_make_null (&old_max_val);
  db_make_null (&old_min_val);
  db_make_null (&new_inc_val);
  db_make_null (&new_max_val);
  db_make_null (&new_min_val);
  db_make_null (&current_val);
  db_make_null (&start_val);
  db_make_null (&class_name_val);
  db_make_null (&abs_inc_val);
  db_make_null (&range_val);
  OID_SET_NULL (&serial_obj_id);

  AU_DISABLE (save);
  /*
   * find _db_serial class
   */
  serial_class = sm_find_class (CT_SERIAL_NAME);
  if (serial_class == NULL)
    {
      error = ER_QPROC_DB_SERIAL_NOT_FOUND;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      goto end;
    }

  /*
   * lookup if serial object name already exists?
   */

  name = (char *) PT_NODE_SR_NAME (statement);

  serial_object = do_get_serial_obj_id (&serial_obj_id, serial_class, name);
  if (serial_object == NULL)
    {
      error = ER_QPROC_SERIAL_NOT_FOUND;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, name);
      PT_ERRORmf (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RT_SERIAL_NOT_DEFINED, name);
      goto end;
    }

  /*
   * after serial.next_value, the currect value maybe changed, but cub_cas
   * still hold the old value. To get the new value. we need decache it
   * then refetch it from server again.
   */
  assert (WS_ISDIRTY (serial_object) == false);

  ws_decache (serial_object);

  /* no need to get the last version for serial - actually, AU_FETCH_WRITE will get only last version, for locking */
  error = au_fetch_instance_force (serial_object, NULL, AU_FETCH_WRITE, LC_FETCH_MVCC_VERSION);
  if (error != NO_ERROR)
    {
      goto end;
    }

  error = db_get (serial_object, SERIAL_ATTR_CLASS_NAME, &class_name_val);
  if (error < 0)
    {
      goto end;
    }

  /*
   * check if user is creator or DBA
   */
  error = au_check_serial_authorization (serial_object);
  if (error != NO_ERROR)
    {
      if (error == ER_QPROC_CANNOT_UPDATE_SERIAL)
    {
      PT_ERRORmf (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RT_SERIAL_ALTER_NOT_ALLOWED, 0);
    }
      goto end;
    }

  /* get old values */
  error = db_get (serial_object, SERIAL_ATTR_CURRENT_VAL, &current_val);
  if (error < 0)
    {
      goto end;
    }

  error = db_get (serial_object, SERIAL_ATTR_INCREMENT_VAL, &old_inc_val);
  if (error < 0)
    {
      goto end;
    }

  error = db_get (serial_object, SERIAL_ATTR_MAX_VAL, &old_max_val);
  if (error < 0)
    {
      goto end;
    }

  error = db_get (serial_object, SERIAL_ATTR_MIN_VAL, &old_min_val);
  if (error < 0)
    {
      goto end;
    }

  error = db_get (serial_object, SERIAL_ATTR_CACHED_NUM, &old_cached_num);
  if (error < 0)
    {
      cached_num = 0;
    }
  else
    {
      cached_num = db_get_int (&old_cached_num);
    }

  /* Now, get new values from node */

  numeric_coerce_string_to_num ("0", 1, INTL_CODESET_ISO88591, &zero);
  numeric_coerce_string_to_num (DB_SERIAL_MAX, strlen (DB_SERIAL_MAX), INTL_CODESET_ISO88591, &e38);
  numeric_coerce_string_to_num (DB_SERIAL_MIN, strlen (DB_SERIAL_MIN), INTL_CODESET_ISO88591, &negative_e38);
  db_make_int (&cmp_result, 0);

  db_value_domain_init (&new_inc_val, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);
  inc_val_node = PT_NODE_SR_INCREMENT_VAL (statement);
  if (inc_val_node != NULL)
    {
      inc_val_change = true;
      pval = pt_value_to_db (parser, inc_val_node);
      if (pval == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }
      error = numeric_db_value_coerce_to_num (pval, &new_inc_val, &data_stat);
      if (error != NO_ERROR)
    {
      goto end;
    }
      pval = NULL;

      error = numeric_db_value_compare (&new_inc_val, &zero, &cmp_result);
      if (error != NO_ERROR)
    {
      goto end;
    }

      new_inc_val_flag = db_get_int (&cmp_result);
      /* new_inc_val == 0 */
      if (new_inc_val_flag == 0)
    {
      error = ER_INVALID_SERIAL_VALUE;
      PT_ERRORmf (parser, statement, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_SERIAL_INC_VAL_ZERO, 0);
      goto end;
    }
    }
  else
    {
      inc_val_change = false;
      /* new_inc_val = old_inc_val; */
      db_value_clone (&old_inc_val, &new_inc_val);
      error = numeric_db_value_compare (&new_inc_val, &zero, &cmp_result);
      if (error != NO_ERROR)
    {
      goto end;
    }

      new_inc_val_flag = db_get_int (&cmp_result);
    }

  /* start_val */
  db_value_domain_init (&start_val, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);
  start_val_node = PT_NODE_SR_START_VAL (statement);
  if (start_val_node != NULL)
    {
      cur_val_change = true;
      pval = pt_value_to_db (parser, start_val_node);
      if (pval == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }
      error = numeric_db_value_coerce_to_num (pval, &start_val, &data_stat);
      if (error != NO_ERROR)
    {
      goto end;
    }
      pval = NULL;
    }
  else
    {
      cur_val_change = false;
      db_value_clone (&current_val, &start_val);
    }

  /* max_val */
  db_value_domain_init (&new_max_val, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);
  max_val_node = PT_NODE_SR_MAX_VAL (statement);
  if (max_val_node != NULL)
    {
      max_val_change = true;
      pval = pt_value_to_db (parser, max_val_node);
      if (pval == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }
      error = numeric_db_value_coerce_to_num (pval, &new_max_val, &data_stat);
      if (error != NO_ERROR)
    {
      goto end;
    }
      pval = NULL;
    }
  else
    {
      if (PT_NODE_SR_NO_MAX (statement) == 1)
    {
      max_val_change = true;
      if (new_inc_val_flag > 0)
        {
          /* new_max_val */
          db_value_clone (&e38, &new_max_val);
        }
      else
        {
          /* new_max_val = -1; */
          db_make_int (&value, -1);
          error = numeric_db_value_coerce_to_num (&value, &new_max_val, &data_stat);
          if (error != NO_ERROR)
        {
          goto end;
        }
        }
    }
      else
    {
      max_val_change = false;
      /* new_max_val = old_max_val; */
      db_value_clone (&old_max_val, &new_max_val);
    }
    }

  /* min_val */
  db_value_domain_init (&new_min_val, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);
  min_val_node = PT_NODE_SR_MIN_VAL (statement);
  if (min_val_node != NULL)
    {
      min_val_change = true;
      pval = pt_value_to_db (parser, min_val_node);
      if (pval == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }
      error = numeric_db_value_coerce_to_num (pval, &new_min_val, &data_stat);
      if (error != NO_ERROR)
    {
      goto end;
    }
      pval = NULL;
    }
  else
    {
      if (PT_NODE_SR_NO_MIN (statement) == 1)
    {
      min_val_change = true;

      if (new_inc_val_flag > 0)
        {
          /* new_min_val = 1; */
          db_make_int (&value, 1);
          error = numeric_db_value_coerce_to_num (&value, &new_min_val, &data_stat);
          if (error != NO_ERROR)
        {
          goto end;
        }
        }
      else
        {
          /* new_min_val */
          db_value_clone (&negative_e38, &new_min_val);
        }
    }
      else
    {
      min_val_change = false;
      /* new_min_val = old_min_val; */
      db_value_clone (&old_min_val, &new_min_val);
    }
    }

  /* cyclic */
  new_cyclic = PT_NODE_SR_CYCLIC (statement);
  if ((new_cyclic == 1) || (PT_NODE_SR_NO_CYCLIC (statement) == 1))
    {
      cyclic_change = true;
    }
  else
    {
      cyclic_change = false;
    }

  /*
   * check values
   * min_val    start_val     max_val
   *    |--^--^--^--o--^--^--^--^---|
   *                   <--> inc_val
   */

  /* invariant for min_val >= negative_e38. */
  initialize_serial_invariant (&invariants[ninvars++], new_min_val, negative_e38, PT_GE,
                   MSGCAT_SEMANTIC_SERIAL_MIN_VAL_UNDERFLOW, 0, ER_QPROC_SERIAL_RANGE_OVERFLOW);

  /* invariant for max_val <= e38. */
  initialize_serial_invariant (&invariants[ninvars++], new_max_val, e38, PT_LE, MSGCAT_SEMANTIC_SERIAL_MAX_VAL_OVERFLOW,
                   0, ER_QPROC_SERIAL_RANGE_OVERFLOW);

  /* invariant for min_val < max_val. */
  initialize_serial_invariant (&invariants[ninvars++], new_min_val, new_max_val, PT_LT,
                   (min_val_change) ? MSGCAT_SEMANTIC_SERIAL_MIN_VAL_INVALID : 0,
                   (max_val_change) ? MSGCAT_SEMANTIC_SERIAL_MAX_VAL_INVALID : 0, ER_INVALID_SERIAL_VALUE);

  /* invariant for min_val <= start_val */
  initialize_serial_invariant (&invariants[ninvars++], new_min_val, start_val, PT_LE,
                   (min_val_change) ? MSGCAT_SEMANTIC_SERIAL_MIN_VAL_INVALID : 0,
                   (cur_val_change) ? MSGCAT_SEMANTIC_SERIAL_START_VAL_INVALID : 0,
                   ER_INVALID_SERIAL_VALUE);

  /* invariant for max_val >= start_val */
  initialize_serial_invariant (&invariants[ninvars++], new_max_val, start_val, PT_GE,
                   (max_val_change) ? MSGCAT_SEMANTIC_SERIAL_MAX_VAL_INVALID : 0,
                   (cur_val_change) ? MSGCAT_SEMANTIC_SERIAL_START_VAL_INVALID : 0,
                   ER_INVALID_SERIAL_VALUE);

  /* invariant for inc_val != zero */
  initialize_serial_invariant (&invariants[ninvars++], new_inc_val, zero, PT_NE, MSGCAT_SEMANTIC_SERIAL_INC_VAL_ZERO, 0,
                   ER_INVALID_SERIAL_VALUE);

  /* invariant for abs(inc_val) <= (max_val - min_val). */
  error = numeric_db_value_sub (&new_max_val, &new_min_val, &range_val);
  if (error == ER_IT_DATA_OVERFLOW)
    {
      // max - min might be flooded. Regard the range is big enough.
      numeric_coerce_string_to_num (DB_SERIAL_MAX, strlen (DB_SERIAL_MAX), INTL_CODESET_ISO88591, &range_val);
      er_clear ();
    }

  db_abs_dbval (&abs_inc_val, &new_inc_val);
  initialize_serial_invariant (&invariants[ninvars++], abs_inc_val, range_val, PT_LE,
                   (inc_val_change) ? MSGCAT_SEMANTIC_SERIAL_INC_VAL_INVALID : 0,
                   (max_val_change) ? MSGCAT_SEMANTIC_SERIAL_MAX_VAL_INVALID :
                   MSGCAT_SEMANTIC_SERIAL_MIN_VAL_INVALID, ER_INVALID_SERIAL_VALUE);

  /* cached num */
  cached_num_node = PT_NODE_SR_CACHED_NUM_VAL (statement);
  if (cached_num_node != NULL)
    {
      DB_VALUE cached_num_int_val, abs_cached_range_val;
      assert (cached_num_node->type_enum == PT_TYPE_INTEGER);

      cached_num_change = true;
      cached_num = cached_num_node->info.value.data_value.i;

      /* ABS (cache_num * inc_val) <= range_val */

      db_make_int (&cached_num_int_val, cached_num);
      db_value_domain_init (&cached_num_val, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);
      error = numeric_db_value_coerce_to_num (&cached_num_int_val, &cached_num_val, &data_stat);
      if (error != NO_ERROR)
    {
      goto end;
    }

      /* ABS (cache_num * inc_val) */
      error = numeric_db_value_mul (&new_inc_val, &cached_num_val, &tmp_val);
      if (error != NO_ERROR)
    {
      goto end;
    }

      error = db_abs_dbval (&abs_cached_range_val, &tmp_val);
      if (error != NO_ERROR)
    {
      goto end;
    }

      initialize_serial_invariant (&invariants[ninvars++], abs_cached_range_val, range_val, PT_LE,
                   MSGCAT_SEMANTIC_SERIAL_CACHED_NUM_INVALID_RANGE, 0, ER_INVALID_SERIAL_VALUE);

      pr_clear_value (&tmp_val);
      pr_clear_value (&abs_cached_range_val);
    }
  else
    {
      if (PT_NODE_SR_NO_CACHE (statement) == 1)
    {
      cached_num_change = true;
      cached_num = 0;
    }
      else
    {
      cached_num_change = false;
    }
    }

  assert (ninvars <= MAX_SERIAL_INVARIANT);
  error = check_serial_invariants (invariants, ninvars, &ret_msg_id);

  if (error != NO_ERROR)
    {
      if (error == ER_QPROC_SERIAL_RANGE_OVERFLOW || error == ER_INVALID_SERIAL_VALUE)
    {
      PT_ERRORmf (parser, statement, MSGCAT_SET_PARSER_SEMANTIC, ret_msg_id, 0);
    }
      goto end;
    }

  obj_tmpl = dbt_edit_object (serial_object);
  if (obj_tmpl == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }

  switch (alter_serial_code)
    {
    case PT_SERIAL_OPTION:
      /* current_val */
      if (cur_val_change)
    {
      error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_CURRENT_VAL, &start_val);
      if (error < 0)
        {
          goto end;
        }
      /* reset started flag because current_val changed */
      db_make_int (&value, 0);
      error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_STARTED, &value);
      if (error < 0)
        {
          goto end;
        }
      pr_clear_value (&value);
    }

      /* increment_val */
      if (inc_val_change)
    {
      error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_INCREMENT_VAL, &new_inc_val);
      if (error < 0)
        {
          goto end;
        }
    }

      /* max_val */
      if (max_val_change)
    {
      error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_MAX_VAL, &new_max_val);
      if (error < 0)
        {
          goto end;
        }
    }

      /* min_val */
      if (min_val_change)
    {
      error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_MIN_VAL, &new_min_val);
      if (error < 0)
        {
          goto end;
        }
    }

      /* cyclic */
      if (cyclic_change)
    {
      db_make_int (&value, new_cyclic);
      error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_CYCLIC, &value);
      if (error < 0)
        {
          goto end;
        }
      pr_clear_value (&value);
    }

      /* cached num */
      if (cached_num_change)
    {

      /* Here we need class_name and att_name to see if this serial is auto_increment. Cause for an auto_increment
       * serial, it is not allowed to change the cached_num for it. */
      if (!DB_IS_NULL (&class_name_val))
        {

          error = MSGCAT_RUNTIME_INVALID_AUTO_INCREMENT_ALTER;

          PT_ERRORmf (parser, statement, MSGCAT_SET_PARSER_RUNTIME, error, name);

          goto end;

        }

      db_make_int (&value, cached_num);
      error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_CACHED_NUM, &value);
      if (error < 0)
        {
          goto end;
        }
      pr_clear_value (&value);
    }
      break;

    case PT_CHANGE_OWNER:
      /* owner to */
      assert (statement->info.serial.owner_name != NULL);
      serial_name = (char *) PT_NODE_SR_NAME (statement);
      serial_owner_name = statement->info.serial.owner_name->info.name.original;

      sm_user_specified_name_for_serial (serial_name, user_specified_serial_name, DB_MAX_SERIAL_NAME_LENGTH);
      serial_mop = do_get_serial_obj_id (&serial_obj_id, serial_class, user_specified_serial_name);
      if (serial_mop == NULL)
    {
      ERROR_SET_ERROR_1ARG (error, ER_QPROC_SERIAL_NOT_FOUND, user_specified_serial_name);
      goto end;
    }

      owner_mop = au_find_user (serial_owner_name);
      if (owner_mop == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

      error = au_change_serial_owner (serial_mop, owner_mop, false);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }
      break;

    default:
      assert (false);
      break;
    }

  /* comment */
  if (statement->info.serial.comment != NULL)
    {
      assert (statement->info.serial.comment->node_type == PT_VALUE);
      comment = (char *) PT_VALUE_GET_BYTES (statement->info.serial.comment);
      db_make_string (&value, comment);
      error = dbt_put_internal (obj_tmpl, SERIAL_ATTR_COMMENT, &value);
      pr_clear_value (&value);
      if (error < 0)
    {
      goto end;
    }
    }

  /* updated_time */
  error = db_update_otmpl_timestamp (obj_tmpl);
  if (error != NO_ERROR)
    {
      goto end;
    }

  serial_object = dbt_finish_object (obj_tmpl);
  if (serial_object == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }

  /* obj_tmpl has been released by dbt_finish_object */
  obj_tmpl = NULL;

end:
  if (!OID_ISNULL (&serial_obj_id))
    {
      (void) serial_decache ((OID *) (&serial_obj_id));
    }

  AU_ENABLE (save);

  if (obj_tmpl != NULL)
    {
      dbt_abort_object (obj_tmpl);
    }

  return error;
}

/*
 * do_drop_serial() -
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in):
 *
 * Note:
 */
int
do_drop_serial (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  DB_OBJECT *serial_class = NULL, *serial_object = NULL;
  DB_IDENTIFIER serial_obj_id;
  DB_VALUE class_name_val;
  char *name;
  int error = NO_ERROR;
  int save;
  bool au_disable_flag = false;

  CHECK_MODIFICATION_ERROR ();

  db_make_null (&class_name_val);
  OID_SET_NULL (&serial_obj_id);

  serial_class = sm_find_class (CT_SERIAL_NAME);
  if (serial_class == NULL)
    {
      error = ER_QPROC_DB_SERIAL_NOT_FOUND;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      goto end;
    }

  name = (char *) PT_NODE_SR_NAME (statement);

  serial_object = do_get_serial_obj_id (&serial_obj_id, serial_class, name);
  if (serial_object == NULL)
    {
      if (statement->info.serial.if_exists)
    {
      return NO_ERROR;
    }
      error = ER_QPROC_SERIAL_NOT_FOUND;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, name);
      PT_ERRORmf (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RT_SERIAL_NOT_DEFINED, name);
      goto end;
    }

  AU_DISABLE (save);
  au_disable_flag = true;

  error = db_get (serial_object, SERIAL_ATTR_CLASS_NAME, &class_name_val);
  if (error < 0)
    {
      goto end;
    }

  if (!DB_IS_NULL (&class_name_val))
    {
      error = ER_QPROC_CANNOT_UPDATE_SERIAL;
      PT_ERRORmf (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_SERIAL_IS_AUTO_INCREMENT_OBJ, name);
      pr_clear_value (&class_name_val);
      goto end;
    }

  /*
   * check if user is creator or DBA
   */
  error = au_check_serial_authorization (serial_object);
  if (error != NO_ERROR)
    {
      if (error == ER_QPROC_CANNOT_UPDATE_SERIAL)
    {
      PT_ERRORmf (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RT_SERIAL_ALTER_NOT_ALLOWED, 0);
    }
      goto end;
    }

  error = db_drop (serial_object);
  if (error < 0)
    {
      goto end;
    }

end:
  if (!OID_ISNULL (&serial_obj_id))
    {
      (void) serial_decache (&serial_obj_id);
    }

  if (au_disable_flag == true)
    {
      AU_ENABLE (save);
    }

  return error;
}



/*
 * Function Group:
 * Entry functions to do execute
 *
 */

#define ER_PT_UNKNOWN_STATEMENT ER_GENERIC_ERROR
#define UNIQUE_SAVEPOINT_EXTERNAL_STATEMENT "eXTERNALsTATEMENT"

bool do_Trigger_involved;

/* do_Trigger_involved does not accurately distinguish
 * whether the corresponding query is a trigger syntax.
 * Therefore, a separate global variable is set to distinguish whether the query is related to a trigger */
bool cdc_Trigger_involved = false;

/*
 * do_statement() -
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in): Parse tree of a statement
 *
 * Note: Side effects can exist at the statement, especially schema information
 */
int
do_statement (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  bool need_stmt_replication = false;
  int suppress_repl_error = NO_ERROR;
  LC_FETCH_VERSION_TYPE read_fetch_instance_version;

  RESERVED_CLASS_INFO *cls_info[64] = { NULL, };
  OID *reserved_oid = NULL;

  /* save old read fetch instance version */
  read_fetch_instance_version = TM_TRAN_READ_FETCH_VERSION ();
  /* If it is an internally created statement, set its host variable info again to search host variables at parent
   * parser */
  SET_HOST_VARIABLES_IF_INTERNAL_STATEMENT (parser);

  if (statement)
    {
      /* skip ddl execution in case of parameter or opt. level */
      if (pt_is_ddl_statement (statement) != 0)
    {
      if (prm_get_bool_value (PRM_ID_BLOCK_DDL_STATEMENT))
        {
          const char *cp = statement->sql_user_text;
          if (cp == NULL)
        {
          cp = statement->alias_print;
        }

          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BLOCK_DDL_STMT, 1, cp ? cp : "unknown");
          error = ER_BLOCK_DDL_STMT;
          goto end;
        }

      /* if QO_PARAM_LEVEL indicate no execution, just return */
      if (qo_need_skip_execution ())
        {
          error = NO_ERROR;
          goto end;
        }
    }

      /* for the subset of nodes which represent top level statements, process them. For any other node, return an
       * error. */

      /* disable data replication log for schema replication log types in HA mode */
      if (!HA_DISABLED () && is_stmt_based_repl_type (statement))
    {
      need_stmt_replication = true;

      /* since we are going to suppress writing replication logs, we need to flush all dirty objects to server not
       * to lose them. */
      error = locator_all_flush ();
      if (error != NO_ERROR)
        {
          /* restore execution flag */
          goto end;
        }

      suppress_repl_error = db_set_suppress_repl_on_transaction (true);
      if (suppress_repl_error != NO_ERROR)
        {
          goto end;
        }
    }

      switch (statement->node_type)
    {
    case PT_GET_TRIGGER:
    case PT_GET_XACTION:
    case PT_SAVEPOINT:
    case PT_PREPARE_TO_COMMIT:
    case PT_COMMIT_WORK:
    case PT_ROLLBACK_WORK:
    case PT_SCOPE:
    case PT_INSERT:
    case PT_SELECT:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
    case PT_UNION:
    case PT_EVALUATE:
    case PT_GET_STATS:
    case PT_GET_OPT_LVL:
    case PT_SET_OPT_LVL:
    case PT_SET_SYS_PARAMS:
    case PT_DO:
    case PT_EXECUTE_PREPARE:
    case PT_VACUUM:
    case PT_QUERY_TRACE:
    case PT_KILL_STMT:
      db_set_read_fetch_instance_version (LC_FETCH_MVCC_VERSION);
      break;

    case PT_EXECUTE_TRIGGER:
    case PT_CREATE_ENTITY:
    case PT_CREATE_INDEX:
    case PT_CREATE_SERIAL:
    case PT_CREATE_TRIGGER:
    case PT_CREATE_USER:
    case PT_ALTER:
    case PT_ALTER_INDEX:
    case PT_ALTER_SERIAL:
    case PT_ALTER_TRIGGER:
    case PT_ALTER_USER:
    case PT_DROP:
    case PT_DROP_INDEX:
    case PT_DROP_SERIAL:
    case PT_DROP_TRIGGER:
    case PT_DROP_USER:
    case PT_DROP_VARIABLE:
    case PT_RENAME:
    case PT_RENAME_TRIGGER:
    case PT_SET_TRIGGER:
    case PT_REMOVE_TRIGGER:
    case PT_GRANT:
    case PT_REVOKE:
    case PT_2PC_ATTACH:
    case PT_SET_XACTION:
    case PT_DELETE:
    case PT_UPDATE:
    case PT_MERGE:
    case PT_METHOD_CALL:
    case PT_UPDATE_STATS:
    case PT_CREATE_STORED_PROCEDURE:
    case PT_ALTER_STORED_PROCEDURE:
    case PT_DROP_STORED_PROCEDURE:
    case PT_TRUNCATE:
    case PT_SET_SESSION_VARIABLES:
    case PT_DROP_SESSION_VARIABLES:
    case PT_SET_NAMES:
    case PT_SET_TIMEZONE:
      /* TODO: check it  */
    case PT_CREATE_SERVER:
    case PT_DROP_SERVER:
    case PT_RENAME_SERVER:
    case PT_ALTER_SERVER:

      /* Need to get dirty version when fetch the instance. That's because we are in an update command. */
      db_set_read_fetch_instance_version (LC_FETCH_DIRTY_VERSION);
      break;

    default:
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PT_UNKNOWN_STATEMENT, 1, statement->node_type);
      break;
    }

      switch (statement->node_type)
    {
    case PT_ALTER:
      error = do_check_internal_statements (parser, statement,
                        /* statement->info.alter. internal_stmts, */
                        do_alter);
      break;

    case PT_2PC_ATTACH:
      error = do_attach (parser, statement);
      break;

    case PT_PREPARE_TO_COMMIT:
      error = do_prepare_to_commit (parser, statement);
      break;

    case PT_COMMIT_WORK:
      error = do_commit (parser, statement);
      break;

    case PT_CREATE_ENTITY:
      error = do_check_internal_statements (parser, statement,
                        /* statement->info.create_entity. internal_stmts, */
                        do_create_entity);
      break;

    case PT_CREATE_INDEX:
      error = do_create_index (parser, statement);
      break;

    case PT_EVALUATE:
      error = do_evaluate (parser, statement);
      break;

    case PT_SCOPE:
      error = do_scope (parser, statement);
      break;

    case PT_DELETE:
      error = do_check_delete_trigger (parser, statement, do_delete);
      break;

    case PT_DROP:
      (void) do_reserve_classinfo (parser, statement, cls_info);

      error = do_check_internal_statements (parser, statement,
                        /* statement->info.drop. internal_stmts, */
                        do_drop);
      break;

    case PT_DROP_INDEX:
      error = do_drop_index (parser, statement);
      break;

    case PT_ALTER_INDEX:
      error = do_alter_index (parser, statement);
      break;

    case PT_DROP_VARIABLE:
      error = do_drop_variable (parser, statement);
      break;

    case PT_GRANT:
      error = do_grant (parser, statement);
      break;

    case PT_INSERT:
      error = do_check_insert_trigger (parser, statement, do_insert);
      break;

    case PT_RENAME:
      error = do_rename (parser, statement);
      break;

    case PT_REVOKE:
      error = do_revoke (parser, statement);
      break;

    case PT_CREATE_USER:
      error = do_create_user (parser, statement);
      break;

    case PT_DROP_USER:
      error = do_drop_user (parser, statement);
      break;

    case PT_ALTER_USER:
      error = do_alter_user (parser, statement);
      break;

    case PT_SET_XACTION:
      error = do_set_xaction (parser, statement);
      break;

    case PT_GET_XACTION:
      error = do_get_xaction (parser, statement);
      break;

    case PT_ROLLBACK_WORK:
      error = do_rollback (parser, statement);
      break;

    case PT_SAVEPOINT:
      error = do_savepoint (parser, statement);
      break;

    case PT_UNION:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
    case PT_SELECT:
      error = do_select (parser, statement);
      break;

    case PT_TRUNCATE:
      error = do_truncate (parser, statement);
      break;

    case PT_DO:
      error = do_execute_do (parser, statement);
      break;

    case PT_UPDATE:
      error = do_check_update_trigger (parser, statement, do_update);
      break;

    case PT_MERGE:
      error = do_check_merge_trigger (parser, statement, do_merge);
      break;

    case PT_UPDATE_STATS:
      error = do_update_stats (parser, statement);
      break;

    case PT_GET_STATS:
      error = do_get_stats (parser, statement);
      break;

    case PT_METHOD_CALL:
      error = do_call_method (parser, statement);
      break;

    case PT_CREATE_TRIGGER:
      error = do_create_trigger (parser, statement);
      break;

    case PT_DROP_TRIGGER:
      error = do_drop_trigger (parser, statement);
      break;

    case PT_SET_TRIGGER:
      error = do_set_trigger (parser, statement);
      break;

    case PT_GET_TRIGGER:
      error = do_get_trigger (parser, statement);
      break;

    case PT_RENAME_TRIGGER:
      error = do_rename_trigger (parser, statement);
      break;

    case PT_ALTER_TRIGGER:
      error = do_alter_trigger (parser, statement);
      break;

    case PT_EXECUTE_TRIGGER:
      error = do_execute_trigger (parser, statement);
      break;

    case PT_REMOVE_TRIGGER:
      error = do_remove_trigger (parser, statement);
      break;

    case PT_CREATE_SERIAL:
      error = do_create_serial (parser, statement);
      break;

    case PT_ALTER_SERIAL:
      error = do_alter_serial (parser, statement);
      break;

    case PT_DROP_SERIAL:
      (void) do_reserve_oidinfo (parser, statement, &reserved_oid);

      error = do_drop_serial (parser, statement);
      break;

    case PT_GET_OPT_LVL:
      error = do_get_optimization_param (parser, statement);
      break;

    case PT_SET_OPT_LVL:
      error = do_set_optimization_param (parser, statement);
      break;

    case PT_SET_SYS_PARAMS:
      error = do_set_sys_params (parser, statement);
      break;

    case PT_CREATE_STORED_PROCEDURE:
      error = jsp_create_stored_procedure (parser, statement);
      break;

    case PT_ALTER_STORED_PROCEDURE:
      error = jsp_alter_stored_procedure (parser, statement);
      break;

    case PT_DROP_STORED_PROCEDURE:
      error = jsp_drop_stored_procedure (parser, statement);
      break;

    case PT_SET_NAMES:
      error = do_set_names (parser, statement);
      break;

    case PT_QUERY_TRACE:
      error = do_set_query_trace (parser, statement);
      break;

    case PT_SET_TIMEZONE:
      error = do_set_timezone (parser, statement);
      break;

    case PT_CREATE_SERVER:
      error = do_create_server (parser, statement);
      break;

    case PT_DROP_SERVER:
      error = do_drop_server (parser, statement);
      break;

    case PT_RENAME_SERVER:
      error = do_rename_server (parser, statement);
      break;

    case PT_ALTER_SERVER:
      error = do_alter_server (parser, statement);
      break;

    default:
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PT_UNKNOWN_STATEMENT, 1, statement->node_type);
      break;
    }

      /* enable data replication log */
      if (need_stmt_replication)
    {
      int repl_error = NO_ERROR;

      /* before enable data replication log, we have to flush all dirty objects to server not to write redundant
       * data replication logs for DDLs */
      if (error >= 0)
        {
          repl_error = locator_all_flush ();
        }

      suppress_repl_error = db_set_suppress_repl_on_transaction (false);

      /* write schema replication log */
      if (error >= 0 && repl_error == NO_ERROR && suppress_repl_error == NO_ERROR)
        {
          repl_error = do_replicate_statement (parser, statement);
        }

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

      if (prm_get_integer_value (PRM_ID_SUPPLEMENTAL_LOG) > 0)
    {
      (void) do_supplemental_statement (parser, statement, cls_info, reserved_oid);
    }
    }

end:
  /* restore old read fetch instance version */
  db_set_read_fetch_instance_version (read_fetch_instance_version);
  /* There may be parse tree fragments that were collected during the execution of the statement that should be freed
   * now. */
  pt_free_orphans (parser);

  /* During query execution, if current transaction was rollbacked by the system, abort transaction on client side
   * also. */
  if (er_errid () == ER_LK_UNILATERALLY_ABORTED)
    {
      error = ER_LK_UNILATERALLY_ABORTED;
    }

  if (error == ER_LK_UNILATERALLY_ABORTED || tran_was_latest_query_aborted ())
    {
      (void) tran_abort_only_client (false);
    }

  RESET_HOST_VARIABLES_IF_INTERNAL_STATEMENT (parser);

  if (error == ER_FAILED)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
    {
      error = ER_GENERIC_ERROR;
    }
    }
  return error;
}

static PT_NODE *
do_clear_subquery_cache_flag (PARSER_CONTEXT * parser, PT_NODE * stmt, void *arg, int *continue_walk)
{
  *continue_walk = PT_CONTINUE_WALK;

  if (PT_IS_QUERY (stmt))
    {
      if (stmt->info.query.flag.subquery_cached)
    {
      stmt->info.query.flag.subquery_cached = false;
    }
    }

  return stmt;
}

static PT_NODE *
do_check_cte_or_system_class_spec (PARSER_CONTEXT * parser, PT_NODE * stmt, void *arg, int *continue_walk)
{
  PT_NODE *q = (PT_NODE *) arg;

  *continue_walk = PT_CONTINUE_WALK;

  if (stmt->node_type != PT_SPEC)
    {
      return stmt;
    }

  if (stmt->info.spec.cte_pointer)
    {
      if (q->info.query.is_subquery == PT_IS_SUBQUERY || q->info.query.is_subquery == PT_IS_UNION_QUERY
      || q->info.query.is_subquery == PT_IS_UNION_SUBQUERY)
    {
      goto stop_walk;
    }
    }

  if (stmt->info.spec.entity_name)
    {
      const char *class_name = stmt->info.spec.entity_name->info.name.original;

      if (class_name)
    {
      if (sm_check_system_class_by_name (class_name))
        {
          goto stop_walk;
        }
    }
    }
  else if (stmt->info.spec.derived_table_type == PT_DERIVED_DBLINK_TABLE)
    {
      goto stop_walk;
    }

  return stmt;

stop_walk:

  q->info.query.flag.do_cache = 0;
  q->info.query.flag.do_not_cache = 1;
  *continue_walk = PT_STOP_WALK;

  return stmt;
}

static PT_NODE *
do_prepare_subquery_pre (PARSER_CONTEXT * parser, PT_NODE * stmt, void *arg, int *continue_walk)
{
  int *err = (int *) arg;
  PT_NODE *saved;

  *continue_walk = PT_CONTINUE_WALK;

  switch (stmt->node_type)
    {
    case PT_UNION:
    case PT_INTERSECTION:
    case PT_DIFFERENCE:
      return stmt;

    case PT_CREATE_TRIGGER:
    case PT_ALTER:
      goto stop_walk;

    case PT_CREATE_ENTITY:
      if (stmt->info.create_entity.entity_type != PT_CLASS)
    {
      goto stop_walk;
    }
      return stmt;

    case PT_SELECT:
      /* 
       * SYSDATE, SERIAL related functions and other queries that should not be cached
       * The parser sets the do_not_cache flag for these queries.
       */
      if (stmt->info.query.flag.do_not_cache)
    {
      return stmt;
    }

      if (stmt->info.query.hint & PT_HINT_QUERY_CACHE)
    {
      /* exclude cache from CTE, system class, or dblink referencing */
      saved = stmt->next;
      stmt->next = NULL;
      parser_walk_tree (parser, stmt, do_check_cte_or_system_class_spec, stmt, NULL, NULL);
      stmt->next = saved;

      if (stmt->info.query.flag.do_not_cache)
        {
          return stmt;
        }
    }
      break;

    default:
      return stmt;
    }

  if ((stmt->info.query.is_subquery == PT_IS_SUBQUERY || stmt->info.query.is_subquery == PT_IS_UNION_QUERY
       || stmt->info.query.is_subquery == PT_IS_UNION_SUBQUERY
       || stmt->info.query.is_subquery == PT_IS_CTE_NON_REC_SUBQUERY) && stmt->info.query.correlation_level == 0
      && (stmt->info.query.hint & PT_HINT_QUERY_CACHE))
    {
      /* 
       * This condition is identical to the one used in parser_print_tree.
       * Both functions must maintain the same condition to ensure consistency.
       * Be careful not to modify only one of them.
       */
      *err = do_prepare_subquery (parser, stmt);

      if (*err != NO_ERROR)
    {
      goto stop_walk;
    }
      else
    {
      /* no more deep walking tree for excluding nested caching */
      *continue_walk = PT_LIST_WALK;
    }
    }

  return stmt;

stop_walk:
  *continue_walk = PT_STOP_WALK;
  return stmt;
}

static int
do_check_subquery_cache (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int err = NO_ERROR;

  if (statement->flag.do_not_use_subquery_cache)
    {
      (void *) parser_walk_tree (parser, statement, do_clear_subquery_cache_flag, NULL, NULL, NULL);
      return NO_ERROR;
    }

  /* All CTE and sub-queries included in the query must be prepared first. */
  if (pt_is_allowed_result_cache ())
    {
      (void *) parser_walk_tree (parser, statement, do_prepare_subquery_pre, &err, NULL, NULL);
    }

  return err;
}

/*
 * do_prepare_statement() - Prepare a given statement for execution
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in): Parse tree of a statement
 *
 * Note:
 *  PREPARE includes query optimization and plan generation (XASL) for the SQL
 *  statement. EXECUTE means requesting the server to execute the given XASL.
 *
 *  Some type of statement is not necessary or not able to do PREPARE stage.
 *  They can or must be EXECUTEd directly without PREPARE. For those types of
 *  statements, this function will return NO_ERROR.
 */
int
do_prepare_statement (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int err = NO_ERROR;

  init_compile_context (parser);

  /* All CTE and sub-queries included in the query must be prepared first. */
  err = do_check_subquery_cache (parser, statement);
  if (err != NO_ERROR)
    {
      return err;
    }

  switch (statement->node_type)
    {
    case PT_DELETE:
      err = do_prepare_delete (parser, statement, NULL);
      break;
    case PT_INSERT:
      err = do_prepare_insert (parser, statement);
      break;
    case PT_UPDATE:
      err = do_prepare_update (parser, statement);
      break;
    case PT_MERGE:
      err = do_prepare_merge (parser, statement);
      break;
    case PT_SELECT:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
    case PT_UNION:
      err = do_prepare_select (parser, statement);
      break;
    case PT_EXECUTE_PREPARE:
      err = do_prepare_session_statement (parser, statement);
      break;
    default:
      /* there are no actions for other types of statements */
      break;
    }

err_exit:
  return ((err == ER_FAILED && (err = er_errid ()) == NO_ERROR) ? ER_GENERIC_ERROR : err);
}               /* do_prepare_statement() */

/*
 * do_execute_statement() - Execute a prepared statement
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in): Parse tree of a statement
 *
 * Note:
 *  The statement should be PREPAREd before to EXECUTE. But, some type of
 *  statement will be EXECUTEd directly without PREPARE stage because we can
 *  decide the fact that they should be executed using query plan (XASL)
 *  at the time of execution stage.
 */
int
do_execute_statement (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int err = NO_ERROR;
  bool need_stmt_based_repl = false;
  int suppress_repl_error;
  LC_FETCH_VERSION_TYPE read_fetch_instance_version;

  RESERVED_CLASS_INFO *cls_info[64] = { NULL, };
  OID *reserved_oid = NULL;

  assert (parser->query_id == NULL_QUERY_ID);
  /* save old read fetch instance version */
  read_fetch_instance_version = TM_TRAN_READ_FETCH_VERSION ();

  /* If it is an internally created statement, set its host variable info again to search host variables at parent
   * parser */
  SET_HOST_VARIABLES_IF_INTERNAL_STATEMENT (parser);

  /* skip ddl execution in case of parameter or opt. level */
  if (pt_is_ddl_statement (statement) != 0)
    {
      if (prm_get_bool_value (PRM_ID_BLOCK_DDL_STATEMENT))
    {
      const char *cp = statement->sql_user_text;
      if (cp == NULL)
        {
          cp = statement->alias_print;
        }

      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BLOCK_DDL_STMT, 1, cp ? cp : "unknown");
      err = ER_BLOCK_DDL_STMT;
      goto end;
    }

      /* if QO_PARAM_LEVEL indicate no execution, just return */
      if (qo_need_skip_execution ())
    {
      err = NO_ERROR;
      goto end;
    }
    }

  /* for the subset of nodes which represent top level statements, process them; for any other node, return an error */

  /* disable data replication log for schema replication log types in HA mode */
  if (!HA_DISABLED () && is_stmt_based_repl_type (statement))
    {
      need_stmt_based_repl = true;

      /* since we are going to suppress writing replication logs, we need to flush all dirty objects to server not to
       * lose them */
      err = locator_all_flush ();
      if (err != NO_ERROR)
    {
      goto end;
    }

      suppress_repl_error = db_set_suppress_repl_on_transaction (true);
      if (suppress_repl_error != NO_ERROR)
    {
      goto end;
    }
    }

  switch (statement->node_type)
    {
    case PT_GET_TRIGGER:
    case PT_GET_XACTION:
    case PT_SAVEPOINT:
    case PT_PREPARE_TO_COMMIT:
    case PT_COMMIT_WORK:
    case PT_ROLLBACK_WORK:
    case PT_SCOPE:
    case PT_INSERT:
    case PT_SELECT:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
    case PT_UNION:
    case PT_EVALUATE:
    case PT_GET_STATS:
    case PT_GET_OPT_LVL:
    case PT_SET_OPT_LVL:
    case PT_SET_SYS_PARAMS:
    case PT_DO:
    case PT_EXECUTE_PREPARE:
    case PT_VACUUM:
    case PT_QUERY_TRACE:
    case PT_KILL_STMT:

      db_set_read_fetch_instance_version (LC_FETCH_MVCC_VERSION);
      break;

    case PT_EXECUTE_TRIGGER:
    case PT_CREATE_ENTITY:
    case PT_CREATE_INDEX:
    case PT_CREATE_SERIAL:
    case PT_CREATE_TRIGGER:
    case PT_CREATE_USER:
    case PT_ALTER:
    case PT_ALTER_INDEX:
    case PT_ALTER_SERIAL:
    case PT_ALTER_TRIGGER:
    case PT_ALTER_USER:
    case PT_DROP:
    case PT_DROP_INDEX:
    case PT_DROP_SERIAL:
    case PT_DROP_TRIGGER:
    case PT_DROP_USER:
    case PT_DROP_VARIABLE:
    case PT_RENAME:
    case PT_RENAME_TRIGGER:
    case PT_SET_TRIGGER:
    case PT_REMOVE_TRIGGER:
    case PT_GRANT:
    case PT_REVOKE:
    case PT_2PC_ATTACH:
    case PT_SET_XACTION:
    case PT_DELETE:
    case PT_UPDATE:
    case PT_MERGE:
    case PT_METHOD_CALL:
    case PT_UPDATE_STATS:
    case PT_CREATE_STORED_PROCEDURE:
    case PT_ALTER_STORED_PROCEDURE:
    case PT_DROP_STORED_PROCEDURE:
    case PT_TRUNCATE:
    case PT_SET_SESSION_VARIABLES:
    case PT_DROP_SESSION_VARIABLES:
    case PT_SET_NAMES:
    case PT_SET_TIMEZONE:

      /* TODO: check it  */
    case PT_CREATE_SERVER:
    case PT_DROP_SERVER:
    case PT_RENAME_SERVER:
    case PT_ALTER_SERVER:

    case PT_ALTER_SYNONYM:
    case PT_CREATE_SYNONYM:
    case PT_DROP_SYNONYM:
    case PT_RENAME_SYNONYM:
      /* Need to get dirty version when fetch the instance. That's because we are in an update command. */
      db_set_read_fetch_instance_version (LC_FETCH_DIRTY_VERSION);
      break;

    default:
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PT_UNKNOWN_STATEMENT, 1, statement->node_type);
      break;
    }

  switch (statement->node_type)
    {
    case PT_CREATE_ENTITY:
      /* err = do_create_entity(parser, statement); */
      /* execute internal statements before and after do_create_entity() */
      err = do_check_internal_statements (parser, statement,
                      /* statement->info.create_entity. internal_stmts, */
                      do_create_entity);
      break;
    case PT_CREATE_INDEX:
      err = do_create_index (parser, statement);
      break;
    case PT_CREATE_SERIAL:
      err = do_create_serial (parser, statement);
      break;
    case PT_CREATE_TRIGGER:
      err = do_create_trigger (parser, statement);
      break;
    case PT_CREATE_USER:
      err = do_create_user (parser, statement);
      break;
    case PT_ALTER:
      /* err = do_alter(parser, statement); */
      /* execute internal statements before and after do_alter() */
      err = do_check_internal_statements (parser, statement,
                      /* statement->info.alter. internal_stmts, */ do_alter);
      break;
    case PT_ALTER_INDEX:
      err = do_alter_index (parser, statement);
      break;
    case PT_ALTER_SERIAL:
      err = do_alter_serial (parser, statement);
      break;
    case PT_ALTER_TRIGGER:
      err = do_alter_trigger (parser, statement);
      break;
    case PT_ALTER_USER:
      err = do_alter_user (parser, statement);
      break;
    case PT_DROP:
      /* err = do_drop(parser, statement); */
      /* execute internal statements before and after do_drop() */

      (void) do_reserve_classinfo (parser, statement, cls_info);
      err = do_check_internal_statements (parser, statement,
                      /* statement->info.drop.internal_stmts, */
                      do_drop);
      break;
    case PT_DROP_INDEX:
      err = do_drop_index (parser, statement);
      break;
    case PT_DROP_SERIAL:
      (void) do_reserve_oidinfo (parser, statement, &reserved_oid);

      err = do_drop_serial (parser, statement);
      break;
    case PT_DROP_TRIGGER:
      err = do_drop_trigger (parser, statement);
      break;
    case PT_DROP_USER:
      err = do_drop_user (parser, statement);
      break;
    case PT_DROP_VARIABLE:
      err = do_drop_variable (parser, statement);
      break;
    case PT_RENAME:
      err = do_rename (parser, statement);
      break;
    case PT_RENAME_TRIGGER:
      err = do_rename_trigger (parser, statement);
      break;
    case PT_SET_TRIGGER:
      err = do_set_trigger (parser, statement);
      break;
    case PT_GET_TRIGGER:
      err = do_get_trigger (parser, statement);
      break;
    case PT_EXECUTE_TRIGGER:
      err = do_execute_trigger (parser, statement);
      break;
    case PT_REMOVE_TRIGGER:
      err = do_remove_trigger (parser, statement);
      break;
    case PT_GRANT:
      err = do_grant (parser, statement);
      break;
    case PT_REVOKE:
      err = do_revoke (parser, statement);
      break;
    case PT_2PC_ATTACH:
      err = do_attach (parser, statement);
      break;
    case PT_GET_XACTION:
      err = do_get_xaction (parser, statement);
      break;
    case PT_SET_XACTION:
      err = do_set_xaction (parser, statement);
      break;
    case PT_SAVEPOINT:
      err = do_savepoint (parser, statement);
      break;
    case PT_PREPARE_TO_COMMIT:
      err = do_prepare_to_commit (parser, statement);
      break;
    case PT_COMMIT_WORK:
      err = do_commit (parser, statement);
      break;
    case PT_ROLLBACK_WORK:
      err = do_rollback (parser, statement);
      break;
    case PT_SCOPE:
      err = do_scope (parser, statement);
      break;
    case PT_DELETE:
      err = do_check_delete_trigger (parser, statement, do_execute_delete);
      break;
    case PT_INSERT:
      err = do_check_insert_trigger (parser, statement, do_execute_insert);
      break;
    case PT_UPDATE:
      err = do_check_update_trigger (parser, statement, do_execute_update);
      break;
    case PT_MERGE:
      err = do_check_merge_trigger (parser, statement, do_execute_merge);
      break;
    case PT_SELECT:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
    case PT_UNION:
      err = do_execute_select (parser, statement);
      break;
    case PT_EVALUATE:
      err = do_evaluate (parser, statement);
      break;
    case PT_METHOD_CALL:
      err = do_call_method (parser, statement);
      break;
    case PT_GET_STATS:
      err = do_get_stats (parser, statement);
      break;
    case PT_UPDATE_STATS:
      err = do_update_stats (parser, statement);
      break;
    case PT_GET_OPT_LVL:
      err = do_get_optimization_param (parser, statement);
      break;
    case PT_SET_OPT_LVL:
      err = do_set_optimization_param (parser, statement);
      break;
    case PT_SET_SYS_PARAMS:
      err = do_set_sys_params (parser, statement);
      break;
    case PT_CREATE_STORED_PROCEDURE:
      err = jsp_create_stored_procedure (parser, statement);
      break;
    case PT_ALTER_STORED_PROCEDURE:
      err = jsp_alter_stored_procedure (parser, statement);
      break;
    case PT_DROP_STORED_PROCEDURE:
      err = jsp_drop_stored_procedure (parser, statement);
      break;
    case PT_TRUNCATE:
      err = do_truncate (parser, statement);
      break;
    case PT_DO:
      err = do_execute_do (parser, statement);
      break;
    case PT_EXECUTE_PREPARE:
      err = do_execute_session_statement (parser, statement);
      break;
    case PT_SET_SESSION_VARIABLES:
      err = do_set_session_variables (parser, statement);
      break;
    case PT_DROP_SESSION_VARIABLES:
      err = do_drop_session_variables (parser, statement);
      break;
    case PT_SET_NAMES:
      err = do_set_names (parser, statement);
      break;
    case PT_VACUUM:
      err = do_vacuum (parser, statement);
      break;
    case PT_QUERY_TRACE:
      err = do_set_query_trace (parser, statement);
      break;
    case PT_KILL_STMT:
      err = do_kill (parser, statement);
      break;
    case PT_SET_TIMEZONE:
      err = do_set_timezone (parser, statement);
      break;
    case PT_CREATE_SERVER:
      err = do_create_server (parser, statement);
      break;
    case PT_DROP_SERVER:
      err = do_drop_server (parser, statement);
      break;
    case PT_RENAME_SERVER:
      err = do_rename_server (parser, statement);
      break;
    case PT_ALTER_SERVER:
      err = do_alter_server (parser, statement);
      break;
    case PT_ALTER_SYNONYM:
      err = do_alter_synonym (parser, statement);
      break;
    case PT_CREATE_SYNONYM:
      err = do_create_synonym (parser, statement);
      break;
    case PT_DROP_SYNONYM:
      err = do_drop_synonym (parser, statement);
      break;
    case PT_RENAME_SYNONYM:
      err = do_rename_synonym (parser, statement);
      break;

    default:
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_PT_UNKNOWN_STATEMENT, 1, statement->node_type);
      break;
    }

  tdes_reset_query_start_info (statement);

  /* enable data replication log */
  if (need_stmt_based_repl)
    {
      int repl_error = NO_ERROR;

      /* before enable data replication log we have to flush all dirty objects to server not to write redundant data
       * replication logs for DDLs and SBR statements */
      if (err >= 0)
    {
      repl_error = locator_all_flush ();
    }

      suppress_repl_error = db_set_suppress_repl_on_transaction (false);

      /* write schema replication log */
      if (err >= 0 && repl_error == NO_ERROR && suppress_repl_error == NO_ERROR)
    {
      repl_error = do_replicate_statement (parser, statement);
    }

      if (repl_error != NO_ERROR)
    {
      err = repl_error;
    }
    }

  if (prm_get_integer_value (PRM_ID_SUPPLEMENTAL_LOG) > 0)
    {
      (void) do_supplemental_statement (parser, statement, cls_info, reserved_oid);
    }

end:

  /* restore old read fetch instance version */
  db_set_read_fetch_instance_version (read_fetch_instance_version);

  /* There may be parse tree fragments that were collected during the execution of the statement that should be freed
   * now. */
  pt_free_orphans (parser);

  /* During query execution, if current transaction was rollbacked by the system, abort transaction on client side
   * also. */
  if (er_errid () == ER_LK_UNILATERALLY_ABORTED)
    {
      err = ER_LK_UNILATERALLY_ABORTED;
    }

  if (err == ER_LK_UNILATERALLY_ABORTED || tran_was_latest_query_aborted ())
    {
      (void) tran_abort_only_client (false);
    }

  RESET_HOST_VARIABLES_IF_INTERNAL_STATEMENT (parser);

  return ((err == ER_FAILED && (err = er_errid ()) == NO_ERROR) ? ER_GENERIC_ERROR : err);
}               /* do_execute_statement() */

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * do_statements() - Execute a prepared statement
 *   return: Error code
 *   parser(in): Parser context
 *   statement_list(in): Parse tree of a statement list
 *
 * Note: Side effects can exist at the statement list
 */
int
do_statements (PARSER_CONTEXT * parser, PT_NODE * statement_list)
{
  int error = 0;
  PT_NODE *statement;

  /* for each of a list of statement nodes, process it. */
  for (statement = statement_list; statement != NULL; statement = statement->next)
    {
      do_Trigger_involved = false;
      error = do_statement (parser, statement);
      do_Trigger_involved = false;
      if (error)
    {
      break;
    }
    }

  return error;
}
#endif

/*
 * do_check_internal_statements() -
 *   return: Error code
 *   parser(in): Parser context
 *   statement_list(in): Parse tree of a statement
 *   internal_stmt_list(in):
 *   do_func(in):
 *
 * Note:
 *   Do savepoint and execute statements before and after do_func()
 *   if an error happens, rollback to the savepoint.
 */
int
do_check_internal_statements (PARSER_CONTEXT * parser, PT_NODE * statement,
                  /* PT_NODE * internal_stmt_list, */
                  PT_DO_FUNC do_func)
{
#if 0               /* to disable TEXT */
  const char *savepoint_name = UNIQUE_SAVEPOINT_EXTERNAL_STATEMENT;
  int error = NO_ERROR, num_rows = NO_ERROR;

  if (internal_stmt_list == NULL)
    {
#endif
      return do_func (parser, statement);
#if 0               /* to disable TEXT */
    }
  else
    {
      error = tran_system_savepoint (savepoint_name);
      if (error != NO_ERROR)
    return error;

      error = do_internal_statements (parser, internal_stmt_list, 0);
      if (error >= NO_ERROR)
    {
      /* The main statement cas use out parameters from internal statements, and the internal statements generate
       * the parameters at execution time. So, it need to bind the parameters again */
      (void) parser_walk_tree (parser, statement, pt_bind_param_node, NULL, NULL, NULL);
      num_rows = error = do_func (parser, statement);
#if defined(CUBRID_DEBUG)
      er_log_debug (ARG_FILE_LINE, "do_check_internal_statements : execute %s statement, %s\n", "main",
            parser_print_tree (parser, statement));
#endif
      if (error >= NO_ERROR)
        {
          error = do_internal_statements (parser, internal_stmt_list, 1);
        }
    }
      if (error < NO_ERROR)
    {
      (void) tran_abort_upto_system_savepoint (savepoint_name);
      return error;
    }
      return num_rows;
    }
#endif
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * do_internal_statements() -
 *   return: Error code
 *   parser(in): Parser context
 *   internal_stmt_list(in):
 *   phase(in):
 *
 * Note:
 *   For input statements, find the statements to do now and
 *   using new parser, parse, check semantics and execute these.
 *
 */
int
do_internal_statements (PARSER_CONTEXT * parser, PT_NODE * internal_stmt_list, const int phase)
{
  PT_NODE *stmt_str;
  DB_QUERY_RESULT *query_result;
  DB_QUERY_ERROR query_error;
  PARSER_CONTEXT *save_parser;
  DB_OBJECT *save_user;
  int au_save;
  int error = NO_ERROR;

  save_user = Au_user;
  Au_user = Au_dba_user;
  AU_DISABLE (au_save);

  for (stmt_str = internal_stmt_list; stmt_str != NULL; stmt_str = stmt_str->next)
    {
      if ((phase == 0 && stmt_str->etc == NULL) || (phase == 1 && stmt_str->etc != NULL))
    {
      /* To get host variable info from parent parser, set the parent parser */
      save_parser = parent_parser;
      parent_parser = parser;
      error = db_compile_and_execute_local (stmt_str->info.value.text, &query_result, &query_error);
      /* restore the parent parser */
      parent_parser = save_parser;
      if (error < NO_ERROR)
        break;
    }
    }

  Au_user = save_user;
  AU_ENABLE (au_save);

  return error;
}
#endif

/*
 * Function Group:
 * Parse tree to update statistics translation.
 *
 */

typedef enum
{
  CST_UNDEFINED,
  CST_NOBJECTS, CST_NPAGES, CST_NATTRIBUTES,
#if 0
  CST_ATTR_MIN, CST_ATTR_MAX,
  CST_ATTR_NINDEXES, CST_BT_NLEAFS, CST_BT_HEIGHT,
#endif
  CST_BT_NKEYS,
} CST_ITEM_ENUM;

typedef struct cst_item CST_ITEM;
struct cst_item
{
  CST_ITEM_ENUM item;
  const char *string;
  int att_id;
  int bt_idx;
};

static CST_ITEM cst_item_tbl[] = {
  {CST_NOBJECTS, "#objects", -1, -1},
  {CST_NPAGES, "#pages", -1, -1},
  {CST_NATTRIBUTES, "#attributes", -1, -1},
#if 0
  {CST_ATTR_MIN, "min", 0, -1},
  {CST_ATTR_MAX, "max", 0, -1},
  {CST_ATTR_NINDEXES, "#indexes", 0, -1},
  {CST_BT_NLEAFS, "#leaf_pages", 0, 0},
  {CST_BT_NPAGES, "#index_pages", 0, 0},
  {CST_BT_HEIGHT, "index_height", 0, 0},
#endif
  {CST_BT_NKEYS, "#keys", 0, 0},
  {CST_UNDEFINED, "", 0, 0}
};

static char *extract_att_name (const char *str);
static int extract_bt_idx (const char *str);
static int make_cst_item_value (DB_OBJECT * obj, const char *str, DB_VALUE * db_val);

/*
 * do_update_stats() - Updates the statistics of a list of classes or ALL classes
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in/out): Parse tree of a update statistics statement
 */
int
do_update_stats (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;

  CHECK_MODIFICATION_ERROR ();

  if (statement->info.update_stats.all_classes > 0)
    {
      // ALL CLASSES
      if (!au_is_dba_group_member (Au_user))
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_AU_DBA_ONLY, 1, "update statistics on all classes");
      return ER_AU_DBA_ONLY;
    }

      error = sm_update_all_statistics (statement->info.update_stats.with_fullscan
                    ? STATS_WITH_FULLSCAN : STATS_WITH_SAMPLING);
      return error;
    }
  else if (statement->info.update_stats.all_classes < 0)
    {
      // CATALOG CLASSES
      if (!au_is_dba_group_member (Au_user))
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_AU_DBA_ONLY, 1, "update statistics on catalog classes");
      return ER_AU_DBA_ONLY;
    }

      error = sm_update_all_catalog_statistics (statement->info.update_stats.with_fullscan
                        ? STATS_WITH_FULLSCAN : STATS_WITH_SAMPLING);
      return error;
    }
  else
    {
      // CLASS LISTS
      PT_NODE *cls = NULL;
      DB_OBJECT *class_mop;
      int class_type;

      // fetch classes and check authorization
      for (cls = statement->info.update_stats.class_list; cls != NULL && error == NO_ERROR; cls = cls->next)
    {
      class_mop = db_find_class (cls->info.name.original);
      if (class_mop != NULL)
        {
          cls->info.name.db_object = class_mop;
          pt_check_user_owns_class (parser, cls);
        }
      else
        {
          ERROR_SET_ERROR_1ARG (error, ER_LC_UNKNOWN_CLASSNAME, cls->info.name.original);
          return error;
        }

      error = au_check_class_authorization (class_mop, AU_ALTER);
      if (error != NO_ERROR)
        {
          // set an error since only warning was set.
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_AU_ALTER_FAILURE, 0);
          return error;
        }
    }

      // update stats
      for (cls = statement->info.update_stats.class_list; cls != NULL && error == NO_ERROR; cls = cls->next)
    {
      class_mop = cls->info.name.db_object;
      class_type = ((SM_CLASS *) class_mop->object)->class_type;

      if (class_type == SM_CLASS_CT)
        {
          error = sm_update_statistics (class_mop, statement->info.update_stats.with_fullscan);
        }
    }

      return error;
    }
}

/*
 * extract_att_name() -
 *   return:
 *   str(in):
 */
static char *
extract_att_name (const char *str)
{
  char *s, *t, *att = NULL;
  int size;

  s = intl_mbs_chr (str, '(');
  if (s && *(++s))
    {
      t = intl_mbs_chr (s, ':');
      if (!t)
    {
      t = intl_mbs_chr (s, ')');
    }
      if (t && t != s)
    {
      size = CAST_STRLEN (t - s);
      att = (char *) malloc (size + 1);
      if (att)
        {
          if (intl_mbs_ncpy (att, s, size + 1) != NULL)
        {
          att[size] = '\0';
        }
          else
        {
          free_and_init (att);
        }
        }
    }
    }
  return att;
}

/*
 * extract_bt_idx() -
 *   return:
 *   str(in):
 */
static int
extract_bt_idx (const char *str)
{
  char *s, *t;
  int idx = -1;

  t = intl_mbs_chr (str, '(');
  if (t && *(++t))
    {
      s = intl_mbs_chr (t, ':');
      if (s && s != t && *(++s))
    {
      t = intl_mbs_chr (s, ')');
      if (t && t != s)
        {
          idx = atoi (s);
        }
    }
    }
  return idx;
}

/*
 * make_cst_item_value() -
 *   return: Error code
 *   obj(in):
 *   str(in):
 *   db_val(in):
 */
static int
make_cst_item_value (DB_OBJECT * obj, const char *str, DB_VALUE * db_val)
{
  CST_ITEM cst_item = { CST_UNDEFINED, "", -1, -1 };
  char *att_name = NULL;
  int bt_idx;
  CLASS_STATS *class_statsp = NULL;
  ATTR_STATS *attr_statsp = NULL;
  BTREE_STATS *bt_statsp = NULL;
  int i;
  int error;

  for (i = 0; i < (signed) DIM (cst_item_tbl); i++)
    {
      if (intl_mbs_ncasecmp (str, cst_item_tbl[i].string, strlen (cst_item_tbl[i].string)) == 0)
    {
      cst_item = cst_item_tbl[i];
      if (cst_item.att_id >= 0)
        {
          att_name = extract_att_name (str);
          if (att_name == NULL)
        {
          cst_item.item = CST_UNDEFINED;
          break;
        }
          cst_item.att_id = sm_att_id (obj, att_name);
          if (cst_item.att_id < 0)
        {
          cst_item.item = CST_UNDEFINED;
          break;
        }
          free_and_init (att_name);
          if (cst_item.bt_idx >= 0)
        {
          bt_idx = extract_bt_idx (str);
          if (bt_idx <= 0)
            {
              cst_item.item = CST_UNDEFINED;
              break;
            }
          cst_item.bt_idx = bt_idx;
        }
        }
      break;
    }
    }
  if (cst_item.item == CST_UNDEFINED)
    {
      db_make_null (db_val);
      error = ER_DO_UNDEFINED_CST_ITEM;
      er_set (ER_SYNTAX_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      return error;
    }

  class_statsp = sm_get_statistics_force (obj);
  if (class_statsp == NULL)
    {
      db_make_null (db_val);

      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }
  if (cst_item.att_id >= 0)
    {
      for (i = 0; i < class_statsp->n_attrs; i++)
    {
      if (class_statsp->attr_stats[i].id == cst_item.att_id)
        {
          attr_statsp = &class_statsp->attr_stats[i];
          break;
        }
    }
    }
  if (attr_statsp && cst_item.bt_idx > 0 && cst_item.bt_idx <= attr_statsp->n_btstats)
    {
      for (i = 0; i < cst_item.bt_idx; i++)
    {
      ;
    }
      bt_statsp = &attr_statsp->bt_stats[i];
    }

  switch (cst_item.item)
    {
    case CST_NOBJECTS:
      db_make_int (db_val, class_statsp->heap_num_objects);
      break;
    case CST_NPAGES:
      db_make_int (db_val, class_statsp->heap_num_pages);
      break;
    case CST_NATTRIBUTES:
      db_make_int (db_val, class_statsp->n_attrs);
      break;
#if 0
    case CST_ATTR_MIN:
      db_make_null (db_val);    /* not support */
      break;
    case CST_ATTR_MAX:
      db_make_null (db_val);    /* not support */
      break;
    case CST_ATTR_NINDEXES:
      if (!attr_statsp)
    {
      db_make_null (db_val);
    }
      else
    {
      db_make_int (db_val, attr_statsp->n_btstats);
    }
      break;
    case CST_BT_NLEAFS:
      if (!attr_statsp || !bt_statsp)
    {
      db_make_null (db_val);
    }
      else
    {
      db_make_int (db_val, bt_statsp->leafs);
    }
      break;
    case CST_BT_NPAGES:
      if (!attr_statsp || !bt_statsp)
    {
      db_make_null (db_val);
    }
      else
    {
      db_make_int (db_val, bt_statsp->pages);
    }
      break;
    case CST_BT_HEIGHT:
      if (!attr_statsp || !bt_statsp)
    {
      db_make_null (db_val);
    }
      else
    {
      db_make_int (db_val, bt_statsp->height);
    }
      break;
#endif
    case CST_BT_NKEYS:
      if (!attr_statsp || !bt_statsp)
    {
      db_make_null (db_val);
    }
      else
    {
      db_make_int (db_val, bt_statsp->keys);
    }
      break;
    default:
      break;
    }

  return NO_ERROR;
}               /* make_cst_item_value() */

/*
 * do_get_stats() -
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in/out): Parse tree of a get statistics statement
 */
int
do_get_stats (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  PT_NODE *cls, *arg, *into;
  DB_OBJECT *obj;
  DB_VALUE *ret_val, db_val;
  int error;

  db_make_null (&db_val);

  cls = statement->info.get_stats.class_;
  arg = statement->info.get_stats.args;
  into = statement->info.get_stats.into_var;
  if (!cls || !arg)
    {
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  obj = db_find_class (cls->info.name.original);
  if (!obj)
    {
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  cls->info.name.db_object = obj;
  (void) pt_check_user_owns_class (parser, cls);

  ret_val = db_value_create ();
  if (ret_val == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  pt_evaluate_tree (parser, arg, &db_val, 1);
  if (pt_has_error (parser) || DB_IS_NULL (&db_val))
    {
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  error = make_cst_item_value (obj, db_get_string (&db_val), ret_val);
  pr_clear_value (&db_val);
  if (error != NO_ERROR)
    {
      return error;
    }

  statement->etc = (void *) ret_val;

  if (into && into->node_type == PT_NAME && into->info.name.original)
    {
      return pt_associate_label_with_value_check_reference (into->info.name.original, db_value_copy (ret_val));
    }

  return NO_ERROR;
}


/*
 * Function Group:
 * DO functions for transaction management
 *
 */

static int map_iso_levels (PARSER_CONTEXT * parser, PT_NODE * statement, DB_TRAN_ISOLATION * tran_isolation,
               PT_NODE * node);
static int set_iso_level (PARSER_CONTEXT * parser, DB_TRAN_ISOLATION * tran_isolation, bool * async_ws,
              PT_NODE * statement, const DB_VALUE * level);
static int check_timeout_value (PARSER_CONTEXT * parser, PT_NODE * statement, DB_VALUE * val);
static const char *get_savepoint_name_from_db_value (DB_VALUE * val);

/*
 * do_attach() - Attaches to named (distributed 2pc) transaction
 *   return: Error code if attach fails
 *   parser(in): Parser context
 *   statement(in): Parse tree of an attach statement
 *
 * Note:
 */
int
do_attach (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  if (!parser || pt_has_error (parser) || !statement || statement->node_type != PT_2PC_ATTACH)
    {
      return ER_GENERIC_ERROR;
    }
  else
    return db_2pc_attach_transaction (statement->info.attach.trans_id);
}

/*
 * do_prepare_to_commit() - Prepare to commit local participant of i
 *              (distributed 2pc) transaction
 *   return: Error code if prepare-to-commit fails
 *   parser(in): Parser context
 *   statement(in): Parse tree of a prepare-to-commit statement
 *
 * Note:
 */
int
do_prepare_to_commit (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  if (!parser || pt_has_error (parser) || !statement || statement->node_type != PT_PREPARE_TO_COMMIT)
    {
      return ER_GENERIC_ERROR;
    }
  else
    return db_2pc_prepare_to_commit_transaction (statement->info.prepare_to_commit.trans_id);
}

/*
 * do_commit() - Commit a transaction
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in): Parse tree of a commit statement
 *
 * Note:
 */
int
do_commit (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  /* Row count should be reset to -1 for explicit commits (i.e: commit statements) but should not be reset in
   * AUTO_COMMIT mode. This is the best place to reset it for commit statements. */
  db_update_row_count_cache (-1);
  cubmethod::get_callback_handler ()->free_query_handle_all (true);
  return tran_commit (statement->info.commit_work.retain_lock ? true : false);
}

/*
 * do_rollback() - Rollbacks a transaction
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in): Parse tree of a rollback statement (for regularity)
 *
 * Note: If a savepoint name is given, the transaction is rolled back to
 *   the savepoint, otherwise the entire transaction is rolled back.
 */
int
do_rollback (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  const char *save_name;
  PT_NODE *name;
  DB_VALUE val;

  db_make_null (&val);

  name = statement->info.rollback_work.save_name;
  if (name == NULL)
    {
      error = tran_abort ();
    }
  else
    {
      if (name->node_type == PT_NAME && name->info.name.meta_class != PT_PARAMETER)
    {
      save_name = name->info.name.original;
      error = db_abort_to_savepoint_internal (save_name);
    }
      else
    {
      pt_evaluate_tree (parser, name, &val, 1);
      if (pt_has_error (parser))
        {
          return ER_GENERIC_ERROR;
        }
      save_name = get_savepoint_name_from_db_value (&val);
      if (save_name == NULL)
        {
          assert (er_errid () != NO_ERROR);
          return er_errid ();
        }
      error = db_abort_to_savepoint_internal (save_name);
      db_value_clear (&val);
    }
    }

  cubmethod::get_callback_handler ()->free_query_handle_all (true);

  return error;
}

/*
 * do_savepoint() - Creates a transaction savepoint
 *   return: Error code if savepoint fails
 *   parser(in): Parser context of a savepoint statement
 *   statement(in): Parse tree of a rollback statement (for regularity)
 *
 * Note: If a savepoint name is given, the savepoint is created
 *   with that name, if no savepoint name is given, we generate a unique one.
 */
int
do_savepoint (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  const char *save_name;
  PT_NODE *name;
  DB_VALUE val;

  db_make_null (&val);

  name = statement->info.savepoint.save_name;
  if (name == NULL)
    {
      PT_INTERNAL_ERROR (parser, "transactions");
    }
  else
    {
      if (name->node_type == PT_NAME && name->info.name.meta_class != PT_PARAMETER)
    {
      save_name = name->info.name.original;
      error = db_savepoint_transaction_internal (save_name);
    }
      else
    {
      pt_evaluate_tree (parser, name, &val, 1);
      if (pt_has_error (parser))
        {
          return ER_GENERIC_ERROR;
        }
      save_name = get_savepoint_name_from_db_value (&val);
      if (save_name == NULL)
        {
          assert (er_errid () != NO_ERROR);
          return er_errid ();
        }
      error = db_savepoint_transaction_internal (save_name);
      db_value_clear (&val);
    }
    }

  return error;
}

/*
 * do_get_xaction() - Gets the isolation level and/or timeout value for
 *                a transaction
 *   return: Error code if it fails
 *   parser(in): Parser context
 *   statement(in/out): Parse tree of a get transaction statement
 *
 * Note:
 */
int
do_get_xaction (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int lock_timeout_in_msecs = 0;
  DB_TRAN_ISOLATION tran_isolation = TRAN_UNKNOWN_ISOLATION;
  bool async_ws;
  int tran_num;
  const char *into_label;
  DB_VALUE *ins_val;
  PT_NODE *into_var;
  int error = NO_ERROR;

  (void) tran_get_tran_settings (&lock_timeout_in_msecs, &tran_isolation, &async_ws);

  /* create a DB_VALUE to hold the result */
  ins_val = db_value_create ();
  if (ins_val == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  db_make_int (ins_val, 0);

  switch (statement->info.get_xaction.option)
    {
    case PT_ISOLATION_LEVEL:
      tran_num = (int) tran_isolation;
      if (async_ws)
    {
      tran_num |= TRAN_ASYNC_WS_BIT;
    }
      db_make_int (ins_val, tran_num);
      break;

    case PT_LOCK_TIMEOUT:
      if (lock_timeout_in_msecs > 0)
    {
      db_make_float (ins_val, (float) lock_timeout_in_msecs / 1000);
    }
      else
    {
      db_make_float (ins_val, (float) lock_timeout_in_msecs);
    }
      break;

    default:
      break;
    }

  statement->etc = (void *) ins_val;

  into_var = statement->info.get_xaction.into_var;
  if (into_var != NULL && into_var->node_type == PT_NAME && (into_label = into_var->info.name.original) != NULL)
    {
      /*
       * create another DB_VALUE of the new instance for
       * the label_table
       */
      ins_val = db_value_create ();
      if (ins_val == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

      db_make_int (ins_val, 0);

      switch (statement->info.get_xaction.option)
    {
    case PT_ISOLATION_LEVEL:
      tran_num = (int) tran_isolation;
      if (async_ws)
        {
          tran_num |= TRAN_ASYNC_WS_BIT;
        }
      db_make_int (ins_val, tran_num);
      break;

    case PT_LOCK_TIMEOUT:
      if (lock_timeout_in_msecs > 0)
        {
          db_make_float (ins_val, (float) (lock_timeout_in_msecs / 1000.0));
        }
      else
        {
          db_make_float (ins_val, (float) lock_timeout_in_msecs);
        }
      break;

    default:
      break;
    }

      /* enter {label, ins_val} pair into the label_table */
      error = pt_associate_label_with_value_check_reference (into_label, ins_val);
    }

  return error;
}

/*
 * do_set_xaction() - Sets the isolation level and/or timeout value for
 *                a transaction
 *   return: Error code if it fails
 *   parser(in): Parser context
 *   statement(in): Parse tree of a set transaction statement
 *
 * Note:
 */
int
do_set_xaction (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  DB_TRAN_ISOLATION tran_isolation;
  DB_VALUE val;
  PT_NODE *mode = statement->info.set_xaction.xaction_modes;
  int error = NO_ERROR;
  bool async_ws;
  float wait_secs;

  db_make_null (&val);

  while ((error == NO_ERROR) && (mode != NULL))
    {
      switch (mode->node_type)
    {
    case PT_ISOLATION_LVL:
      if (mode->info.isolation_lvl.level == NULL)
        {
          /* map schema/instance pair to level */
          error = map_iso_levels (parser, statement, &tran_isolation, mode);
          async_ws = mode->info.isolation_lvl.async_ws ? true : false;
        }
      else
        {
          pt_evaluate_tree (parser, mode->info.isolation_lvl.level, &val, 1);

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

          if (DB_VALUE_TYPE (&val) != DB_TYPE_INTEGER)
        {
          PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_MVCC_RUNTIME_XACT_ISO_LVL_MSG);
          return ER_GENERIC_ERROR;
        }

          error = set_iso_level (parser, &tran_isolation, &async_ws, statement, &val);
        }

      if (error == NO_ERROR)
        {
          prm_set_integer_value (PRM_ID_LOG_ISOLATION_LEVEL, (int) tran_isolation);
          error = tran_reset_isolation (tran_isolation, async_ws);
        }
      break;
    case PT_TIMEOUT:
      pt_evaluate_tree (parser, mode->info.timeout.val, &val, 1);
      if (pt_has_error (parser))
        {
          return ER_GENERIC_ERROR;
        }

      if (check_timeout_value (parser, statement, &val) != NO_ERROR)
        {
          return ER_GENERIC_ERROR;
        }
      else
        {
          wait_secs = db_get_float (&val);
          prm_set_integer_value (PRM_ID_LK_TIMEOUT, wait_secs);
          if (wait_secs > 0)
        {
          wait_secs *= 1000;
        }
          (void) tran_reset_wait_times ((int) wait_secs);
        }
      break;
    default:
      return ER_GENERIC_ERROR;
    }

      mode = mode->next;
    }

  return error;
}

/*
 * do_get_optimization_level() - Determine the current optimization and
 *               return it through the statement parameter.
 *   return: Error code if it fails
 *   parser(in): Parser context
 *   statement(in/out): Parse tree of a get transaction statement
 *
 * Note:
 */
int
do_get_optimization_param (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  DB_VALUE *val;
  PT_NODE *into_var;
  const char *into_name;
  char *cost;
  int error = NO_ERROR;

  val = db_value_create ();
  if (val == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  switch (statement->info.get_opt_lvl.option)
    {
    case PT_OPT_LVL:
      {
    int i;
    qo_get_optimization_param (&i, QO_PARAM_LEVEL);
    db_make_int (val, i);
    break;
      }
    case PT_OPT_COST:
      {
    DB_VALUE plan;

    db_make_null (&plan);

    pt_evaluate_tree (parser, statement->info.get_opt_lvl.args, &plan, 1);
    if (pt_has_error (parser))
      {
        return ER_OBJ_INVALID_ARGUMENTS;
      }

    /* 'cost' is referenced by 'val', it should be allocated from heap, and will be freed when free 'val' if set
     * 'need_clear' to 'true' */
    cost = (char *) db_private_alloc (NULL, 2);
    if (cost == NULL)
      {
        return ER_OUT_OF_VIRTUAL_MEMORY;
      }

    qo_get_optimization_param (cost, QO_PARAM_COST, db_get_string (&plan));
    pr_clear_value (&plan);
    db_make_string (val, cost);
    val->need_clear = true;
      }
    default:
      /*
       * Default ok; nothing else can get in here.
       */
      break;
    }

  statement->etc = (void *) val;

  into_var = statement->info.get_opt_lvl.into_var;
  if (into_var != NULL && into_var->node_type == PT_NAME && (into_name = into_var->info.name.original) != NULL)
    {
      error = pt_associate_label_with_value_check_reference (into_name, db_value_copy (val));
    }

  return error;
}

/*
 * do_set_optimization_param() - Set the optimization level to the indicated
 *               value and return the old value through the
 *               statement parameter.
 *   return: Error code if it fails
 *   parser(in): Parser context
 *   statement(in): Parse tree of a set transaction statement
 *
 * Note:
 */
int
do_set_optimization_param (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  PT_NODE *p1, *p2;
  DB_VALUE val1, val2;
  const char *plan, *cost;

  db_make_null (&val1);
  db_make_null (&val2);

  p1 = statement->info.set_opt_lvl.val;

  if (p1 == NULL)
    {
      er_set (ER_ERROR_SEVERITY, __FILE__, __LINE__, ER_OBJ_INVALID_ARGUMENTS, 0);
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  pt_evaluate_tree (parser, p1, &val1, 1);
  if (pt_has_error (parser))
    {
      pr_clear_value (&val1);
      return NO_ERROR;
    }

  switch (statement->info.set_opt_lvl.option)
    {
    case PT_OPT_LVL:
      {
    int level = db_get_int (&val1);
    if (CHECK_INVALID_OPTIMIZATION_LEVEL (level))
      {
        pr_clear_value (&val1);
        er_set (ER_ERROR_SEVERITY, __FILE__, __LINE__, ER_OBJ_INVALID_ARGUMENTS, 0);
        return ER_OBJ_INVALID_ARGUMENTS;
      }
    qo_set_optimization_param (NULL, QO_PARAM_LEVEL, (int) db_get_int (&val1));
      }
      break;
    case PT_OPT_COST:
      plan = db_get_string (&val1);
      p2 = p1->next;
      pt_evaluate_tree (parser, p2, &val2, 1);
      if (pt_has_error (parser))
    {
      pr_clear_value (&val1);
      pr_clear_value (&val2);
      return ER_OBJ_INVALID_ARGUMENTS;
    }
      switch (DB_VALUE_TYPE (&val2))
    {
    case DB_TYPE_INTEGER:
      qo_set_optimization_param (NULL, QO_PARAM_COST, plan, db_get_int (&val2));
      break;
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      cost = db_get_string (&val2);
      qo_set_optimization_param (NULL, QO_PARAM_COST, plan, (int) cost[0]);
      break;
    default:
      er_set (ER_ERROR_SEVERITY, __FILE__, __LINE__, ER_OBJ_INVALID_ARGUMENTS, 0);
      pr_clear_value (&val1);
      pr_clear_value (&val2);
      return ER_OBJ_INVALID_ARGUMENTS;
    }
      break;
    default:
      /*
       * Default ok; no other options available.
       */
      break;
    }

  pr_clear_value (&val1);
  pr_clear_value (&val2);
  return NO_ERROR;
}

/*
 * do_set_sys_params() - Set the system parameters defined in 'cubrid.conf'.
 *   return: Error code if it fails
 *   parser(in): Parser context
 *   statement(in): Parse tree of a set transaction statement
 *
 * Note:
 */
int
do_set_sys_params (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  PT_NODE *val;
  DB_VALUE db_val;
  int error = NO_ERROR;

  db_make_null (&db_val);

  val = statement->info.set_sys_params.val;
  if (val == NULL)
    {
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  db_make_null (&db_val);
  while (val && error == NO_ERROR)
    {
      pt_evaluate_tree (parser, val, &db_val, 1);

      if (pt_has_error (parser))
    {
      error = ER_GENERIC_ERROR;
    }
      else
    {
      error = db_set_system_parameters (db_get_string (&db_val));
    }

      pr_clear_value (&db_val);
      val = val->next;
    }

  return error;
}

/*
 * map_iso_levels() - Maps the schema/instance isolation level to the
 *                DB_TRAN_ISOLATION enumerated type.
 *   return: Error code if it fails
 *   parser(in): Parser context
 *   tran_isolation(out):
 *   node(in): Parse tree of a set transaction statement
 *
 * Note: Initializes isolation_levels array
 */
static int
map_iso_levels (PARSER_CONTEXT * parser, PT_NODE * statement, DB_TRAN_ISOLATION * tran_isolation, PT_NODE * node)
{
  PT_MISC_TYPE instances = node->info.isolation_lvl.instances;
  PT_MISC_TYPE schema = node->info.isolation_lvl.schema;

  switch (schema)
    {
    case PT_SERIALIZABLE:
      if (instances == PT_SERIALIZABLE)
    {
      *tran_isolation = TRAN_SERIALIZABLE;
    }
      else
    {
      PT_ERRORmf2 (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_XACT_INVALID_ISO_LVL_MSG,
               pt_show_misc_type (schema), pt_show_misc_type (instances));
      return ER_GENERIC_ERROR;
    }
      break;
    case PT_REPEATABLE_READ:
      if (instances == PT_READ_COMMITTED)
    {
      *tran_isolation = TRAN_READ_COMMITTED;
    }
      else if (instances == PT_REPEATABLE_READ)
    {
      *tran_isolation = TRAN_REPEATABLE_READ;
    }
      else
    {
      PT_ERRORmf2 (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_XACT_INVALID_ISO_LVL_MSG,
               pt_show_misc_type (schema), pt_show_misc_type (instances));
      return ER_GENERIC_ERROR;
    }
      break;
    case PT_READ_COMMITTED:
      if (instances == PT_READ_COMMITTED)
    {
      *tran_isolation = TRAN_READ_COMMITTED;
    }
      else
    {
      PT_ERRORmf2 (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_XACT_INVALID_ISO_LVL_MSG,
               pt_show_misc_type (schema), pt_show_misc_type (instances));
      return ER_GENERIC_ERROR;
    }
      break;
    default:
      return ER_GENERIC_ERROR;
    }

  return NO_ERROR;
}

/*
 * set_iso_level() -
 *   return: Error code if it fails
 *   parser(in): Parser context
 *   tran_isolation(out): Isolation level set as a side effect
 *   async_ws(out):
 *   statement(in): Parse tree of a set transaction statement
 *   level(in):
 *
 * Note: Translates the user entered isolation level (1,2,3,4,5) into
 *       the enumerated type.
 */
static int
set_iso_level (PARSER_CONTEXT * parser, DB_TRAN_ISOLATION * tran_isolation, bool * async_ws, PT_NODE * statement,
           const DB_VALUE * level)
{
  int error = NO_ERROR;
  int isolvl = db_get_int (level) & 0x0F;
  *async_ws = (db_get_int (level) & 0xF0) ? true : false;

  /* translate to the enumerated type */
  switch (isolvl)
    {
    case TRAN_READ_COMMITTED:
      *tran_isolation = TRAN_READ_COMMITTED;
      fprintf (stdout,
           msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_ISO_LVL_SET_TO_MSG));
      fprintf (stdout,
           msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_REPREAD_S_READCOM_I));
      break;
    case TRAN_REPEATABLE_READ:
      *tran_isolation = TRAN_REPEATABLE_READ;
      fprintf (stdout,
           msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_ISO_LVL_SET_TO_MSG));
      fprintf (stdout,
           msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_REPREAD_S_REPREAD_I));
      break;
    case TRAN_SERIALIZABLE:
      *tran_isolation = TRAN_SERIALIZABLE;
      fprintf (stdout,
           msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_ISO_LVL_SET_TO_MSG));
      fprintf (stdout,
           msgcat_message (MSGCAT_CATALOG_CUBRID, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_SERIAL_S_SERIAL_I));
      break;
    case 0:
      if (*async_ws == true)
    {           /* only async workspace is given */
      int dummy_lktimeout;
      bool dummy_aws;
      tran_get_tran_settings (&dummy_lktimeout, tran_isolation, &dummy_aws);
      break;
    }
      [[fallthrough]];
    case 1:         /* unsupported ones */
    case 2:
    case 3:
    default:
      PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_MVCC_RUNTIME_XACT_ISO_LVL_MSG);
      error = ER_GENERIC_ERROR;
    }

  return error;
}

/*
 * check_timeout_value() -
 *   return: Error code if it fails
 *   parser(in): Parser context
 *   statement(in):
 *   val(in): DB_VALUE with the value to set
 *
 * Note: Checks the user entered isolation level. Valid values are:
 *                    -1 : Infinite
 *                     0 : Don't wait
 *                    >0 : Wait this number of seconds
 */
static int
check_timeout_value (PARSER_CONTEXT * parser, PT_NODE * statement, DB_VALUE * val)
{
  float timeout;

  if (db_value_coerce (val, val, &tp_Float_domain) == DOMAIN_COMPATIBLE)
    {
      timeout = db_get_float (val);
      if ((timeout == -1) || (timeout >= 0))
    {
      return NO_ERROR;
    }
    }
  PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_TIMEOUT_VALUE_MSG);
  return ER_GENERIC_ERROR;
}

/*
 * get_savepoint_name_from_db_value() -
 *   return: a NULL if the value doesn't properly describe the name
 *           of a savepoint.
 *   val(in):
 *
 * Note: Mutates the contents of val to hold a NULL terminated string
 *       holding a valid savepoint name.  If the value is already of
 *       type string, a NULL termination will be assumed since the
 *       name came from a parse tree.
 */
const static char *
get_savepoint_name_from_db_value (DB_VALUE * val)
{
  if (DB_VALUE_TYPE (val) != DB_TYPE_CHAR && DB_VALUE_TYPE (val) != DB_TYPE_VARCHAR)
    {
      if (tp_value_cast (val, val, tp_domain_resolve_default (DB_TYPE_VARCHAR), false) != DOMAIN_COMPATIBLE)
    {
      return (char *) NULL;
    }
    }

  return db_get_string (val);
}





/*
 * Function Group:
 * DO functions for trigger management
 *
 */


/* Value supplied in statement has an invalid type */
#define ER_TR_INVALID_VALUE_TYPE ER_GENERIC_ERROR

#define MAX_DOMAIN_NAME_SIZE 150

/*
 * PARSE TREE MACROS
 *
 * arguments:
 *  statement: parser node
 *
 * returns/side-effects: non-zero
 *
 * description:
 *    These are used as shorthand for parse tree access.
 *    Given a statement node, they test for certain characteristics
 *    and return a boolean.
 */

#define IS_REJECT_ACTION_STATEMENT(statement) \
  ((statement)->node_type == PT_TRIGGER_ACTION \
   && (statement)->info.trigger_action.action_type == PT_REJECT)

#define IS_INVALIDATE_ACTION_STATEMENT(statement) \
  ((statement)->node_type == PT_TRIGGER_ACTION \
   && (statement)->info.trigger_action.action_type == PT_INVALIDATE_XACTION)

#define IS_PRINT_ACTION_STATEMENT(statement) \
  ((statement)->node_type == PT_TRIGGER_ACTION \
   && (statement)->info.trigger_action.action_type == PT_PRINT)

#define PT_NODE_TR_NAME(node) \
 ((node)->info.create_trigger.trigger_name->info.name.original)
#define PT_NODE_TR_STATUS(node) \
 (convert_misc_to_tr_status((node)->info.create_trigger.trigger_status))
#define PT_NODE_TR_PRI(node) \
  ((node)->info.create_trigger.trigger_priority)
#define PT_NODE_TR_EVENT_TYPE(node) \
  (convert_event_to_tr_event((node)->info.create_trigger.trigger_event->info.event_spec.event_type))

#define PT_NODE_TR_TARGET(node) \
  ((node)->info.create_trigger.trigger_event->info.event_spec.event_target)
#define PT_TR_TARGET_CLASS(target) \
  ((target)->info.event_target.class_name->info.name.original)
#define PT_TR_TARGET_ATTR(target) \
  ((target)->info.event_target.attribute)
#define PT_TR_ATTR_NAME(attr) \
  ((attr)->info.name.original)

#define PT_NODE_COND(node) \
  ((node)->info.create_trigger.trigger_condition)
#define PT_NODE_COND_TIME(node) \
  (convert_misc_to_tr_time((node)->info.create_trigger.condition_time))

#define PT_NODE_ACTION(node) \
  ((node)->info.create_trigger.trigger_action)
#define PT_NODE_ACTION_TIME(node) \
  (convert_misc_to_tr_time((node)->info.create_trigger.action_time))

#define PT_NODE_TR_REF(node) \
  ((node)->info.create_trigger.trigger_reference)
#define PT_TR_REF_REFERENCE(ref) \
  (&(ref)->info.event_object)

static int tr_savepoint_number = 0;

static int merge_mop_list_extension (DB_OBJLIST * new_objlist, DB_OBJLIST ** list);
static DB_TRIGGER_EVENT convert_event_to_tr_event (const PT_EVENT_TYPE ev);
static DB_TRIGGER_TIME convert_misc_to_tr_time (const PT_MISC_TYPE pt_time);
static DB_TRIGGER_STATUS convert_misc_to_tr_status (const PT_MISC_TYPE pt_status);
static int convert_speclist_to_objlist (DB_OBJLIST ** triglist, PT_NODE * specnode);
static int check_trigger (DB_TRIGGER_EVENT event, PT_DO_FUNC * do_func, PARSER_CONTEXT * parser, PT_NODE * statement);
static int check_merge_trigger (PT_DO_FUNC * do_func, PARSER_CONTEXT * parser, PT_NODE * statement);
static char **find_update_columns (int *count_ptr, PT_NODE * statement);
static void get_activity_info (PARSER_CONTEXT * parser, DB_TRIGGER_ACTION * type, const char **source,
                   PT_NODE * statement);

/*
 * merge_mop_list_extension() -
 *   return: Number of MOPs to be added
 *   new(in):
 *   list(in):
 *
 * Note:
 */
static int
merge_mop_list_extension (DB_OBJLIST * new_objlist, DB_OBJLIST ** list)
{
  DB_OBJLIST *obj, *next;
  int added = 0;

  for (obj = new_objlist, next = NULL; obj != NULL; obj = next)
    {
      next = obj->next;
      if (ml_find (*list, obj->op))
    {
      obj->next = NULL;
      ml_ext_free (obj);
    }
      else
    {
      obj->next = *list;
      *list = obj;
      added++;
    }
    }

  return added;
}

/*
 * These translate parse tree things into corresponding trigger things.
 */

/*
 * convert_event_to_tr_event() - Converts a PT_EV type into the corresponding
 *               TR_EVENT_ type.
 *   return: DB_TRIGER_EVENT
 *   ev(in): One of PT_EVENT_TYPE
 *
 * Note:
 */
static DB_TRIGGER_EVENT
convert_event_to_tr_event (const PT_EVENT_TYPE ev)
{
  DB_TRIGGER_EVENT event = TR_EVENT_NULL;

  switch (ev)
    {
    case PT_EV_INSERT:
      event = TR_EVENT_INSERT;
      break;
    case PT_EV_STMT_INSERT:
      event = TR_EVENT_STATEMENT_INSERT;
      break;
    case PT_EV_DELETE:
      event = TR_EVENT_DELETE;
      break;
    case PT_EV_STMT_DELETE:
      event = TR_EVENT_STATEMENT_DELETE;
      break;
    case PT_EV_UPDATE:
      event = TR_EVENT_UPDATE;
      break;
    case PT_EV_STMT_UPDATE:
      event = TR_EVENT_STATEMENT_UPDATE;
      break;
    case PT_EV_ALTER:
      event = TR_EVENT_ALTER;
      break;
    case PT_EV_DROP:
      event = TR_EVENT_DROP;
      break;
    case PT_EV_COMMIT:
      event = TR_EVENT_COMMIT;
      break;
    case PT_EV_ROLLBACK:
      event = TR_EVENT_ROLLBACK;
      break;
    case PT_EV_ABORT:
      event = TR_EVENT_ABORT;
      break;
    case PT_EV_TIMEOUT:
      event = TR_EVENT_TIMEOUT;
      break;
    default:
      break;
    }

  return event;
}

/*
 * convert_misc_to_tr_time() - Converts a PT_MISC_TYPE into a corresponding
 *                     TR_TYPE_TYPE constant.
 *   return: DB_TRIGGER_TIME
 *   pt_time(in): One of PT_MISC_TYPE
 *
 * Note:
 */
static DB_TRIGGER_TIME
convert_misc_to_tr_time (const PT_MISC_TYPE pt_time)
{
  DB_TRIGGER_TIME time;

  switch (pt_time)
    {
    case PT_AFTER:
      time = TR_TIME_AFTER;
      break;
    case PT_BEFORE:
      time = TR_TIME_BEFORE;
      break;
    case PT_DEFERRED:
      time = TR_TIME_DEFERRED;
      break;
    default:
      time = TR_TIME_NULL;
      break;
    }

  return time;
}

/*
 * convert_misc_to_tr_status() - Converts a PT_MISC_TYPE into the corresponding
 *               TR_STATUE_TYPE.
 *   return: DB_TRIGGER_STATUS
 *   pt_status(in): One of PT_MISC_TYPE
 *
 * Note:
 */
static DB_TRIGGER_STATUS
convert_misc_to_tr_status (const PT_MISC_TYPE pt_status)
{
  DB_TRIGGER_STATUS status;

  switch (pt_status)
    {
    case PT_ACTIVE:
      status = TR_STATUS_ACTIVE;
      break;
    case PT_INACTIVE:
      status = TR_STATUS_INACTIVE;
      break;
    default:            /* if we get bogus input, should it be inactive ? */
      status = TR_STATUS_ACTIVE;
      break;
    }

  return status;
}

/*
 * convert_speclist_to_objlist() - Converts a PT_MISC_TYPE into the
 *                 corresponding TR_STATUE_TYPE.
 *   return: Error code
 *   triglist(out): Returned trigger object list
 *   specnode(in): Node with PT_TRIGGER_SPEC_LIST_INFO
 *
 * Note:
 *    This function converts a trigger specification list in PT format
 *    into a list of the corresponding trigger objects.
 *    This is used by a variety of the functions that accept trigger
 *    specifications.
 *    The list is an external MOP list and must be freed with ml_ext_free()
 *    or db_objlist_free.
 *    The alter flag is set for operations that alter triggers based
 *    on the WITH EVENT and ALL TRIGGERS specification.  In these cases
 *    we need to automatically filter out the triggers in the list for
 *    which we don't have authorization.
 */
static int
convert_speclist_to_objlist (DB_OBJLIST ** triglist, PT_NODE * specnode)
{
  int error = NO_ERROR;
  DB_OBJLIST *triggers, *etrigs;
  PT_NODE *names, *n, *events, *e;
  PT_EVENT_SPEC_INFO *espec;
  PT_EVENT_TARGET_INFO *target;
  const char *str, *attribute;
  DB_TRIGGER_EVENT tr_event;
  DB_OBJECT *trigger, *class_;

  triggers = NULL;

  if (specnode != NULL)
    {
      if (specnode->info.trigger_spec_list.all_triggers)
    {
      error = tr_find_all_triggers (&triggers);
    }

      else if ((names = specnode->info.trigger_spec_list.trigger_name_list) != NULL)
    {
      /* since this is an explicitly specified list, if we do not have alter authorization for any of the specified
       * triggers, we need to make sure the statement is not executed (no triggers are dropped). Use
       * tr_check_authorization to find out. */
      for (n = names; n != NULL && error == NO_ERROR; n = n->next)
        {
          str = n->info.name.original;
          trigger = tr_find_trigger (str);
          if (trigger == NULL)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
        }
          else
        {
          error = ml_ext_add (&triggers, trigger, NULL);
        }
        }
    }
      else if ((events = specnode->info.trigger_spec_list.event_list) != NULL)
    {
      for (e = events; e != NULL && error == NO_ERROR; e = e->next)
        {
          class_ = NULL;
          attribute = NULL;
          espec = &(e->info.event_spec);
          tr_event = convert_event_to_tr_event (espec->event_type);

          if (espec->event_target != NULL)
        {
          target = &(espec->event_target->info.event_target);
          class_ = db_find_class (target->class_name->info.name.original);
          if (class_ == NULL)
            {
              assert (er_errid () != NO_ERROR);
              error = er_errid ();
            }
          else
            {
              if (target->attribute != NULL)
            {
              attribute = target->attribute->info.name.original;
            }

              error = tr_find_event_triggers (tr_event, class_, attribute, true, &etrigs);
              if (error == NO_ERROR)
            {
              merge_mop_list_extension (etrigs, &triggers);
            }
            }
        }
        }
    }
    }

  if (error)
    {
      ml_ext_free (triggers);
    }
  else
    {
      *triglist = triggers;
    }

  return error;
}

/*
 * get_priority() -
 *   return: Double value
 *   parser(in): Parser context
 *   node(in): Priority value node
 *
 * Note:
 *    Shorthand function for getting the priority value out of the parse
 *    tree.  Formerly, we just assumed that this would be represented
 *    with a double value.  Now we use coersion.
 */
static double
get_priority (PARSER_CONTEXT * parser, PT_NODE * node)
{
  DB_VALUE *src, value;
  double priority;

  priority = TR_LOWEST_PRIORITY;

  src = pt_value_to_db (parser, node);
  if (src != NULL && (tp_value_coerce (src, &value, &tp_Double_domain) == DOMAIN_COMPATIBLE))
    {
      priority = db_get_double (&value);
    }
  /* else, should be setting some kind of error */

  return priority;
}

/*
 * INSERT, UPDATE, & DELETE STATEMENTS
 */

/*
 * check_trigger() -
 *   return: Error code
 *   event(in): Trigger event type
 *   do_func(in): Function to do
 *   parser(in): Parser context used by do_func
 *   statement(in): Parse tree of a statement used by do_func
 *
 * Note: The function checks if there is any active trigger defined on
 *       the targets. If there is one, raise the trigger. Otherwise,
 *       perform the given do_ function.
 */
static int
check_trigger (DB_TRIGGER_EVENT event, PT_DO_FUNC * do_func, PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int err, result = NO_ERROR;
  TR_STATE *state;
  const char *savepoint_name = NULL;
  PT_NODE *node = NULL, *flat = NULL;
  DB_OBJECT *class_ = NULL;

  /* Prepare a trigger state for any triggers that must be raised in this statement */

  state = NULL;

  switch (event)
    {
    case TR_EVENT_STATEMENT_DELETE:
      node = statement->info.delete_.spec;
      while (node != NULL)
    {
      if (node->info.spec.flag & PT_SPEC_FLAG_DELETE)
        {
          flat = node->info.spec.flat_entity_list;
          if (node->info.spec.remote_server_name)
        {
          result = NO_ERROR;
        }
          else
        {
          class_ = (flat ? flat->info.name.db_object : NULL);
          if (class_ == NULL)
            {
              PT_INTERNAL_ERROR (parser, "invalid spec id");
              result = ER_FAILED;
              goto exit;
            }
          result = tr_prepare_statement (&state, event, class_, 0, NULL);
          if (result != NO_ERROR)
            {
              goto exit;
            }
        }
        }
      node = node->next;
    }
      break;

    case TR_EVENT_STATEMENT_INSERT:
      flat = (statement->info.insert.spec) ? statement->info.insert.spec->info.spec.flat_entity_list : NULL;

      if (statement->info.insert.spec && statement->info.insert.spec->info.spec.flat_entity_list == NULL
      && statement->info.insert.spec->info.spec.remote_server_name)
    {
      result = NO_ERROR;
    }
      else
    {
      class_ = (flat) ? flat->info.name.db_object : NULL;
      result = tr_prepare_statement (&state, event, class_, 0, NULL);
    }
      break;

    case TR_EVENT_STATEMENT_UPDATE:
      {
    /* If this is an "update object" statement, we may not have a spec list yet. This may have been fixed due to
     * the recent changes in pt_exec_trigger_stmt to do name resolution each time. */
    char **columns = NULL;
    int count = pt_count_assignments (parser, statement->info.update.assignment);
    int idx;
    PT_ASSIGNMENTS_HELPER ea;
    PT_NODE *assign = NULL;
    PT_SPEC_FLAG flag;

    /* do not check it in case of dblink */
    if (statement->info.update.spec && statement->info.update.spec->info.spec.remote_server_name)
      {
        result = NO_ERROR;
        break;
      }

    columns = (char **) (malloc (count * sizeof (char *)));
    if (columns == NULL)
      {
        er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, count * sizeof (char *));
        result = ER_FAILED;
        goto exit;
      }

    /* prepare trigger state structures */
    node = statement->info.update.spec;
    do
      {
        /* flag is set to UPDATE to make sure triggers are checked for statement->info.update.object_parameter too */
        flag = PT_SPEC_FLAG_UPDATE;

        if (node != NULL)
          {
        flat = node->info.spec.flat_entity_list;
        flag = node->info.spec.flag;
        node = node->next;
          }
        else
          {
        flat = statement->info.update.object_parameter;
          }

        if (flag & PT_SPEC_FLAG_UPDATE)
          {
        idx = 0;
        pt_init_assignments_helper (parser, &ea, statement->info.update.assignment);
        while ((assign = pt_get_next_assignment (&ea)) != NULL)
          {
            if (assign->info.name.spec_id == flat->info.name.spec_id)
              {
            columns[idx++] = (char *) assign->info.name.original;
              }
          }

        class_ = flat ? flat->info.name.db_object : NULL;
        if (class_ == NULL)
          {
            PT_INTERNAL_ERROR (parser, "invalid spec id");
            result = ER_FAILED;
            goto exit;
          }

        result = tr_prepare_statement (&state, event, class_, idx, (const char **) columns);
          }
      }
    while (node);
    if (columns)
      {
        free_and_init (columns);
      }
    break;
      }
    default:
      break;
    }

  if (result == NO_ERROR)
    {
      if (state == NULL)
    {
      /* no triggers, just do it */
      /* result = do_func(parser, statement); */
      /* execute internal statements before and after do_func() */
      result = do_check_internal_statements (parser, statement, do_func);
    }
      else
    {
      /* the operations performed in 'tr_before', 'do_check_internal_statements' and 'tr_after' should be all
       * contained in one transaction */
      if (tr_Current_depth <= 1)
        {
          savepoint_name = mq_generate_name (parser, "UtrP", &tr_savepoint_number);
          if (savepoint_name == NULL)
        {
          result = ER_GENERIC_ERROR;
          goto exit;
        }
          result = tran_system_savepoint (savepoint_name);
          if (result != NO_ERROR)
        {
          goto exit;
        }
        }

      /* fire BEFORE STATEMENT triggers */
      result = tr_before (state);
      if (result == NO_ERROR)
        {
          /* note, do_insert, do_update, & do_delete don't return just errors, they can also return positive result
           * counts.  Need to specifically check for result < 0 */
          /* result = do_func(parser, statement); */
          /* execute internal statements before and after do_func() */
          result = do_check_internal_statements (parser, statement, do_func);
          if (result < NO_ERROR)
        {
          tr_abort (state);
          state = NULL; /* state was freed */
        }
          else
        {
          /* try to preserve the usual result value */
          err = tr_after (state);
          if (err)
            {
              result = err;
            }
          if (tr_get_execution_state ())
            {
              state = NULL; /* state was freed */
            }

        }
        }
      else
        {
          state = NULL;
        }
    }
    }

exit:
  if (state)
    {
      /* We need to free state and decrease the tr_Current_depth. */
      tr_abort (state);
    }

  if (result < NO_ERROR && savepoint_name != NULL && (result != ER_LK_UNILATERALLY_ABORTED))
    {
      /* savepoint from tran_savepoint() */
      (void) tran_abort_upto_system_savepoint (savepoint_name);
    }
  return result;
}

/*
 * do_check_for_empty_classes_in_delete() - check empty tables
 *   return: Error code, NO_ERROR or 1 if there is at least one empty class
 *   parser(in): Parser context
 *   statement(in): Delete statement
 *
 * Note:  The function checks if the original join, which was splitted, would
 *    have returned 0 elements. If so then the original DELETE statement
 *    would have deleted no records. After split this behaviour will change.
 *    So we check that there is at least one splitted table with no records.
 *    For compatibility reasons we must preserve the behaviour of the
 *    original DELETE statement.
 */
static int
do_check_for_empty_classes_in_delete (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR, num_classes = 0, idx, partition_type = 0;
  PT_NODE *node = statement->info.delete_.del_stmt_list, *flat = NULL;
  char **classes_names = NULL;
  LOCK *locks = NULL;
  int *need_subclasses = NULL, au_save = 0;
  MOP *partitions = NULL;
  HFID *hfid = NULL;
  bool has_rows = false;
  LC_PREFETCH_FLAGS *flags = NULL;

  /* count the number of new DELETE statements */
  while (node != NULL)
    {
      num_classes++;
      node = node->next;
    }

  /* allocate classes_names array */
  classes_names = (char **) db_private_alloc (NULL, num_classes * sizeof (char *));
  if (classes_names == NULL)
    {
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto cleanup;
    }

  /* allocate locks array */
  locks = (LOCK *) db_private_alloc (NULL, num_classes * sizeof (LOCK));
  if (locks == NULL)
    {
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto cleanup;
    }

  /* allocate need_subclasses array */
  need_subclasses = (int *) db_private_alloc (NULL, num_classes * sizeof (int));
  if (need_subclasses == NULL)
    {
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto cleanup;
    }

  flags = (LC_PREFETCH_FLAGS *) db_private_alloc (NULL, num_classes * sizeof (LC_PREFETCH_FLAGS));
  if (flags == NULL)
    {
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto cleanup;
    }

  /* prepare information for locking */
  node = statement->info.delete_.del_stmt_list;
  for (idx = 0; idx < num_classes && node != NULL; idx++, node = node->next)
    {
      if (node->info.delete_.spec == NULL || node->info.delete_.spec->info.spec.entity_name == NULL
      || node->info.delete_.spec->info.spec.entity_name->info.name.original == NULL)
    {
      error = ER_GENERIC_ERROR;
      goto cleanup;
    }
      classes_names[idx] = (char *) db_private_alloc (NULL, SM_MAX_IDENTIFIER_LENGTH * sizeof (char));
      sm_downcase_name (node->info.delete_.spec->info.spec.entity_name->info.name.original, classes_names[idx],
            SM_MAX_IDENTIFIER_LENGTH);
      locks[idx] = X_LOCK;
      if (node->info.delete_.spec->info.spec.only_all == PT_ALL)
    {
      need_subclasses[idx] = true;
    }
      else
    {
      need_subclasses[idx] = false;
    }
      flags[idx] = LC_PREF_FLAG_LOCK;
    }

  /* lock splitted classes with X_LOCK */
  if (locator_lockhint_classes (num_classes, (const char **) classes_names, locks, need_subclasses, flags, 1, NULL_LOCK)
      != LC_CLASSNAME_EXIST)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto cleanup;
    }

  AU_DISABLE (au_save);
  /* Check if we have a splitted spec that has no records */
  for (node = statement->info.delete_.del_stmt_list; node != NULL; node = node->next)
    {
      flat = node->info.delete_.spec->info.spec.flat_entity_list;
      if (flat == NULL)
    {
      error = ER_GENERIC_ERROR;
      goto cleanup;
    }

      has_rows = false;
      /* we check subclasses and partitions including the class itself */
      do
    {
      error = locator_flush_all_instances (flat->info.name.db_object, DONT_DECACHE);
      if (error != NO_ERROR)
        {
          goto cleanup;
        }

      error = sm_partitioned_class_type (flat->info.name.db_object, &partition_type, NULL, &partitions);
      if (error != NO_ERROR)
        {
          goto cleanup;
        }
      if (partition_type == DB_PARTITIONED_CLASS && partitions != NULL)
        {
          for (idx = 0; partitions[idx] != NULL && !has_rows; idx++)
        {
          hfid = sm_get_ch_heap (partitions[idx]);
          if (hfid == NULL)
            {
              free_and_init (partitions);
              goto cleanup;
            }
          error = heap_has_instance (hfid, ws_oid (partitions[idx]), 1);
          if (error < NO_ERROR)
            {
              free_and_init (partitions);
              goto cleanup;
            }
          if (error > 0)
            {
              has_rows = true;
            }
        }
        }
      else
        {
          hfid = sm_get_ch_heap (flat->info.name.db_object);
          if (hfid == NULL)
        {
          goto cleanup;
        }
          error = heap_has_instance (hfid, ws_oid (flat->info.name.db_object), 1);
          if (error < NO_ERROR)
        {
          goto cleanup;
        }
          if (error > 0)
        {
          has_rows = true;
        }
        }
      if (partitions != NULL)
        {
          free_and_init (partitions);
        }
      flat = flat->next;
    }
      while (flat != NULL && !has_rows);
      if (!has_rows)
    {
      break;
    }
    }

  /* if we have a splitted class from wich all records will be deleted and it has no records then the join will have no
   * records so we can abort the deletion. */
  error = (node == NULL ? NO_ERROR : 1);

cleanup:

  AU_ENABLE (au_save);

  /* free allocated resources */
  if (classes_names != NULL)
    {
      for (idx = 0; idx < num_classes; idx++)
    {
      db_private_free_and_init (NULL, classes_names[idx]);
    }
      db_private_free_and_init (NULL, classes_names);
    }

  if (locks != NULL)
    {
      db_private_free (NULL, locks);
    }

  if (need_subclasses != NULL)
    {
      db_private_free (NULL, need_subclasses);
    }

  if (flags != NULL)
    {
      db_private_free (NULL, flags);
    }

  return error;
}

/*
 * do_check_delete_trigger() -
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in): Parse tree of a statement
 *   do_func(in): Function to do
 *
 * Note: The function checks if there is any active trigger with event
 *   TR_EVENT_STATEMENT_DELETE defined on the target.
 *   If there is one, raise the trigger. Otherwise, perform the
 *   given do_ function.
 */
int
do_check_delete_trigger (PARSER_CONTEXT * parser, PT_NODE * statement, PT_DO_FUNC * do_func)
{
  PT_NODE *node = NULL;
  int affected_count, error = 0;
  PT_NODE *next = NULL;

  if (prm_get_bool_value (PRM_ID_BLOCK_NOWHERE_STATEMENT) && statement->info.delete_.search_cond == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BLOCK_NOWHERE_STMT, 0);
      return ER_BLOCK_NOWHERE_STMT;
    }

  if (statement->info.delete_.del_stmt_list != NULL)
    {
      error = do_check_for_empty_classes_in_delete (parser, statement);
      if (error < 0)
    {
      return error;
    }
      if (error > 0)
    {
      return 0;
    }
    }

  if (statement->flag.use_auto_commit)
    {
      /* No active trigger is involved. Avoid lock and fetch request. */
      error = do_func (parser, statement);
    }
  else
    {
      error = check_trigger (TR_EVENT_STATEMENT_DELETE, do_func, parser, statement);
    }

  /* if the statement that contains joins with conditions deletes no record then we skip the deletion in the subsequent
   * classes beacuse the original join would have deleted no record */
  if (error <= NO_ERROR)
    {
      return error;
    }

  affected_count = error;

  if (!statement->flag.use_auto_commit)
    {
      node = statement->info.delete_.del_stmt_list;
      while (node != NULL)
    {
      next = node->next;
      node->next = NULL;
      error = check_trigger (TR_EVENT_STATEMENT_DELETE, do_func, parser, node);
      node->next = next;
      if (error < NO_ERROR)
        {
          return error;
        }
      affected_count += error;

      node = node->next;
    }
    }

  return affected_count;
}

/*
 * do_check_insert_trigger() -
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in): Parse tree of a statement
 *   do_func(in): Function to do
 *
 * Note: The function checks if there is any active trigger with event
 *   TR_EVENT_STATEMENT_INSERT defined on the target.
 *   If there is one, raise the trigger. Otherwise, perform the
 *   given do_ function.
 */
int
do_check_insert_trigger (PARSER_CONTEXT * parser, PT_NODE * statement, PT_DO_FUNC * do_func)
{
  if (statement->flag.use_auto_commit)
    {
      /* no active trigger is involved. Avoid lock and fetch request. */
      return do_func (parser, statement);
    }
  else
    {
      return check_trigger (TR_EVENT_STATEMENT_INSERT, do_func, parser, statement);
    }
}

/*
 * find_update_columns() -
 *   return: Attribute (column) name array
 *   count_ptr(out): Returned name count
 *   statement(in): Parse tree of a statement to examine
 *
 * Note:
 *    This is used to to find the attribute/column names referenced in
 *    the statement.  It builds a array of strings and returns the length of
 *    the array.
 */
static char **
find_update_columns (int *count_ptr, PT_NODE * statement)
{
  PT_NODE *assign;
  char **columns;
  int count, size, i;
  PT_NODE *lhs, *att;

  assign = statement->info.update.assignment;
  for (count = 0; assign; assign = assign->next)
    {
      lhs = assign->info.expr.arg1;
      if (PT_IS_N_COLUMN_UPDATE_EXPR (lhs))
    {
      /* multicolumn update */
      count += pt_length_of_list (lhs->info.expr.arg1);
    }
      else
    {
      count++;
    }
    }
  size = sizeof (char *) * count;

  columns = (char **) (malloc (size));
  if (columns == NULL)
    {
      return NULL;
    }

  assign = statement->info.update.assignment;
  for (i = 0; i < count; assign = assign->next)
    {
      lhs = assign->info.expr.arg1;
      if (PT_IS_N_COLUMN_UPDATE_EXPR (lhs))
    {
      for (att = lhs->info.expr.arg1; att; att = att->next)
        {
          columns[i++] = (char *) att->info.name.original;
        }
    }
      else
    {
      columns[i++] = (char *) lhs->info.name.original;
    }
    }

  *count_ptr = count;
  return columns;
}

/*
 * do_check_update_trigger() -
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in): Parse tree of a statement
 *
 * Note: The function checks if there is any active trigger with event
 *   TR_EVENT_STATEMENT_UPDATE defined on the target.
 *   If there is one, raise the trigger. Otherwise, perform the
 *   given do_ function.
 */
int
do_check_update_trigger (PARSER_CONTEXT * parser, PT_NODE * statement, PT_DO_FUNC * do_func)
{
  int err;

  if (prm_get_bool_value (PRM_ID_BLOCK_NOWHERE_STATEMENT) && statement->info.update.search_cond == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BLOCK_NOWHERE_STMT, 0);
      return ER_BLOCK_NOWHERE_STMT;
    }

  if (statement->flag.use_auto_commit)
    {
      /* no active trigger is involved. Avoid lock and fetch request. */
      err = do_func (parser, statement);
    }
  else
    {
      err = check_trigger (TR_EVENT_STATEMENT_UPDATE, do_func, parser, statement);
    }

  return err;
}

/*
 * CREATE TRIGGER STATEMENT
 */

/*
 * get_activity_info() - Works for do_create_trigger
 *   return: None
 *   parser(in): Parse context for the create trigger statement
 *   type(out): Returned type of the activity
 *   source(out) : Returned source of the activity (sometimes NULL)
 *   statement(in): Sub-tree for the condition or action expression
 *
 * Note:
 *    This is used to convert a parser sub-tree into the corresponding
 *    pair of DB_TRIGGER_ACTIVITY and source string suitable for use
 *    with tr_create_trigger.
 *    Since we can't use this parsed representation of the expressions
 *    anyway (they aren't inside the proper scope), we just convert
 *    them back into strings with parser_print_tree and let the trigger manager
 *    call pt_compile_trigger_stmt when necessary.
 */
static void
get_activity_info (PARSER_CONTEXT * parser, DB_TRIGGER_ACTION * type, const char **source, PT_NODE * statement)
{
  PT_NODE *str;
  unsigned int save_custom;

  *type = TR_ACT_NULL;
  *source = NULL;

  if (statement != NULL)
    {
      if (IS_REJECT_ACTION_STATEMENT (statement))
    {
      *type = TR_ACT_REJECT;
    }
      else if (IS_INVALIDATE_ACTION_STATEMENT (statement))
    {
      *type = TR_ACT_INVALIDATE;
    }
      else if (IS_PRINT_ACTION_STATEMENT (statement))
    {
      *type = TR_ACT_PRINT;

      /* extract the print string from the parser node, not sure if I should be looking at the "data_value.s" field
       * or the "text" field, they seem to be the same always. */
      str = statement->info.trigger_action.string;
      if (str->node_type == PT_VALUE)
        {
          *source = (char *) str->info.value.data_value.str->bytes;
        }
    }
      else
    {
      /* complex expression */
      *type = TR_ACT_EXPRESSION;
      save_custom = parser->custom_print;
      *source = parser_print_tree_with_quotes (parser, statement);
      parser->custom_print = save_custom;
    }
    }
}

/*
 * do_create_trigger() -
 *   return: Error code
 *   parser(in): Parse context
 *   statement(in): Parse tree of a statement
 *
 * Note: The function creates a trigger object by calling the trigger
 *   create function.
 */
int
do_create_trigger (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  PT_NODE *cond, *action, *target, *attr, *pri, *comment_node;
  const char *name, *comment;
  DB_TRIGGER_STATUS status;
  double priority;
  DB_TRIGGER_EVENT event;
  DB_OBJECT *class_;
  const char *attribute;
  DB_TRIGGER_ACTION cond_type, action_type;
  DB_TRIGGER_TIME cond_time, action_time;
  const char *cond_source, *action_source;
  DB_OBJECT *trigger;
  SM_CLASS *smclass = NULL;
  int error = NO_ERROR;
  CHECK_MODIFICATION_ERROR ();

  name = PT_NODE_TR_NAME (statement);
  status = PT_NODE_TR_STATUS (statement);

  comment_node = statement->info.create_trigger.comment;
  if (comment_node != NULL)
    {
      assert (comment_node->node_type == PT_VALUE);
      comment = (char *) comment_node->info.value.data_value.str->bytes;
    }
  else
    {
      comment = NULL;
    }

  pri = PT_NODE_TR_PRI (statement);
  if (pri != NULL)
    {
      priority = get_priority (parser, pri);
    }
  else
    {
      priority = TR_LOWEST_PRIORITY;
    }

  event = PT_NODE_TR_EVENT_TYPE (statement);
  class_ = NULL;
  attribute = NULL;
  target = PT_NODE_TR_TARGET (statement);
  if (target)
    {
      class_ = db_find_class (PT_TR_TARGET_CLASS (target));
      if (class_ == NULL)
    {
      ERROR_SET_ERROR_1ARG (error, ER_LC_UNKNOWN_CLASSNAME, PT_TR_TARGET_CLASS (target));
      return error;
    }
#if defined (ENABLE_UNUSED_FUNCTION)    /* to disable TEXT */
      if (sm_has_text_domain (db_get_attributes (class_), 1))
    {
      /* prevent to create a trigger at the class to contain TEXT */
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_REGU_NOT_IMPLEMENTED, 1, rel_major_release_string ());
      return er_errid ();
    }
#endif /* ENABLE_UNUSED_FUNCTION */
      attr = PT_TR_TARGET_ATTR (target);
      if (attr)
    {
      attribute = PT_TR_ATTR_NAME (attr);
    }
      error = au_fetch_class (class_, &smclass, AU_FETCH_READ, AU_SELECT);
      if (error != NO_ERROR)
    {
      return error;
    }
      if (smclass->partition != NULL && smclass->users == NULL)
    {
      /* Triggers must be created on the partitioned table, not on a specific partition */
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_INVALID_PARTITION_REQUEST, 0);
      return ER_INVALID_PARTITION_REQUEST;
    }
    }
  cond = PT_NODE_COND (statement);
  cond_time = PT_NODE_COND_TIME (statement);
  /* note that cond_type can only be TR_ACT_EXPRESSION, if there is no conditino node, cond_source will be left NULL */
  get_activity_info (parser, &cond_type, &cond_source, cond);

  action = PT_NODE_ACTION (statement);
  action_time = PT_NODE_ACTION_TIME (statement);
  get_activity_info (parser, &action_type, &action_source, action);

  trigger =
    tr_create_trigger (name, status, priority, event, class_, attribute, cond_time, cond_source, action_time,
               action_type, action_source, comment);

  if (trigger == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  /* Save the new trigger object in the parse tree. Actually, we probably should also allow INTO variable sub-clause to
   * be compatible with INSERT statement. In that case, the portion of code in do_insert() for saving the new object and
   * creating a label table entry needs to be made a extern function. */

  /* This should be treated like a "create class" statement not like an "insert" statement.  The trigger object that
   * gets created can't be assigned with an INTO clause so there's no need to return it. Assuming this doesn't host
   * anything, delete the commented out lines below. */
#if 0
  if ((value = db_value_create ()) == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }
  db_make_object (value, trigger);
  statement->etc = (void *) value;
#endif

  if (smclass != NULL && smclass->users != NULL && TM_TRAN_ISOLATION () < TRAN_REP_READ)
    {
      /* We have to flush the newly created trigger if the class it belongs to has subclasses. This is because the same
       * trigger is assigned to the whole hierarchy and we have to make sure it does not remain a temporary object when
       * it is first compiled. Since the class that this trigger belongs to might also be a temporary object, we
       * actually have to flush the whole workspace. No need to flush in isolation levels >= repeatable read since
       * already flushed in tr_create_trigger */
      error = locator_all_flush ();
    }

  return error;
}

/*
 *  MISC TRIGGER OPERATIONS
 */

/*
 * do_drop_trigger() - Drop one or more triggers based on a trigger spec list.
 *   return: Error code
 *   parser(in): Parse context
 *   statement(in): Parse tree of a statement
 *
 * Note:
 */
int
do_drop_trigger (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  PT_NODE *speclist;
  DB_OBJLIST *triggers, *t;

  CHECK_MODIFICATION_ERROR ();

  /* The grammar has beem define such that DROP TRIGGER can only be used with an explicit list of named triggers.
   * Although convert_speclist_to_objlist will handle the WITH EVENT and ALL TRIGGERS cases we shouldn't see those
   * here.  If for some reason they do sneak in, we may get errors when we call tr_drop_triggger() on triggers we don't
   * own. */

  speclist = statement->info.drop_trigger.trigger_spec_list;
  if (convert_speclist_to_objlist (&triggers, speclist))
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  if (triggers != NULL)
    {
      /* make sure we have ALTER authorization on all the triggers before proceeding */

      for (t = triggers; t != NULL && error == NO_ERROR; t = t->next)
    {
      error = tr_check_authorization (t->op, true);
    }

      if (error == NO_ERROR)
    {
      /* shouldn't encounter errors in this loop, if we do, may have to abort the transaction */
      for (t = triggers; t != NULL && error == NO_ERROR; t = t->next)
        {
          error = tr_drop_trigger (t->op, false);
          if (error == ER_TR_TRIGGER_NOT_FOUND)
        {
          /* another transaction has drop the trigger before me */
          break;
        }
        }
    }

      /* always free this */
      ml_ext_free (triggers);
    }

  return error;
}

/*
 * do_alter_trigger() - Alter the priority or status of one or more triggers.
 *   return: Error code
 *   parser(in): Parse context
 *   statement(in): Parse tree with alter trigger node
 *
 * Note:
 */
int
do_alter_trigger (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  PT_NODE *speclist = NULL, *p_node = NULL, *comment_node = NULL;
  DB_OBJLIST *triggers = NULL, *t = NULL;
  double priority = TR_LOWEST_PRIORITY;
  DB_TRIGGER_STATUS status;
  PT_NODE *trigger_owner = NULL, *trigger_name = NULL;
  const char *trigger_owner_name = NULL, *trigger_comment = NULL;
  bool has_trigger_comment = false;
  TR_TRIGGER *trigger = NULL;
  int count;
  bool has_savepoint = false;
  MOP trigger_mop = NULL, owner_mop = NULL;

  CHECK_MODIFICATION_ERROR ();

  triggers = NULL;
  p_node = statement->info.alter_trigger.trigger_priority;
  trigger_owner = statement->info.alter_trigger.trigger_owner;
  speclist = statement->info.alter_trigger.trigger_spec_list;
  if (convert_speclist_to_objlist (&triggers, speclist))
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  comment_node = statement->info.alter_trigger.comment;
  if (comment_node != NULL)
    {
      has_trigger_comment = true;
      assert (comment_node->node_type == PT_VALUE);
      trigger_comment = (char *) comment_node->info.value.data_value.str->bytes;
    }

  /* currently, we can' set the status and priority at the same time. The existance of p_node determines which type of
   * alter statement this is. */
  status = TR_STATUS_INVALID;

  if (trigger_owner != NULL)
    {
      trigger_owner_name = trigger_owner->info.name.original;
      trigger_name = speclist->info.trigger_spec_list.trigger_name_list;
    }
  else if (p_node != NULL)
    {
      priority = get_priority (parser, p_node);
    }
  else if (statement->info.alter_trigger.trigger_status == PT_ACTIVE
       || statement->info.alter_trigger.trigger_status == PT_INACTIVE)
    {
      status = convert_misc_to_tr_status (statement->info.alter_trigger.trigger_status);
    }
  else
    {
      /* here, means user intends to alter comment only, which must exist */
      assert (has_trigger_comment);
    }

  if (error == NO_ERROR)
    {
      /* make sure we have ALTER authorization on all the triggers before proceeding */
      count = 0;
      for (t = triggers; t != NULL && error == NO_ERROR; t = t->next)
    {
      error = tr_check_authorization (t->op, true);
      count++;
    }

      if (error == NO_ERROR)
    {
      if (count > 0 || TM_TRAN_ISOLATION () >= TRAN_REP_READ)
        {
          /* need atomic operation */
          error = tran_system_savepoint (UNIQUE_SAVEPOINT_ALTER_TRIGGER);
          if (error != NO_ERROR)
        {
          goto cleanup;
        }
          has_savepoint = true;
        }

      for (t = triggers; t != NULL && error == NO_ERROR; t = t->next)
        {
          if (status != TR_STATUS_INVALID)
        {
          error = tr_set_status (t->op, status, false);
          if (error != NO_ERROR)
            {
              ASSERT_ERROR ();
              break;
            }
        }

          if (p_node != NULL)
        {
          error = tr_set_priority (t->op, priority, false);
          if (error != NO_ERROR)
            {
              ASSERT_ERROR ();
              break;
            }
        }

          if (trigger_owner != NULL)
        {
          assert (trigger_name != NULL);

          owner_mop = au_find_user (trigger_owner_name);
          if (owner_mop == NULL)
            {
              ASSERT_ERROR_AND_SET (error);
              break;
            }

          error = au_change_trigger_owner (t->op, owner_mop);
          if (error != NO_ERROR)
            {
              ASSERT_ERROR ();
              break;
            }

          trigger_name = trigger_name->next;
        }

          if (has_trigger_comment)
        {
          error = tr_set_comment (t->op, trigger_comment, false);
          if (error != NO_ERROR)
            {
              ASSERT_ERROR ();
              break;
            }
        }

          error = tr_update_trigger_timestamp (t->op);
          if (error != NO_ERROR)
        {
          ASSERT_ERROR ();
          break;
        }

          error = locator_flush_instance (t->op);
          if (error != NO_ERROR)
        {
          ASSERT_ERROR ();
          break;
        }

          /* If trigger is on a class, lock it (S_LOCK) to block concurrent DML statements. */
          trigger = tr_map_trigger (t->op, false);
          if (trigger->class_mop != NULL)
        {
          if (locator_fetch_class (trigger->class_mop, DB_FETCH_SCAN) == NULL)
            {
              ASSERT_ERROR_AND_SET (error);
              break;
            }
        }
        }
    }
    }

  if (has_savepoint && error != NO_ERROR && error != ER_LK_UNILATERALLY_ABORTED)
    {
      (void) tran_abort_upto_system_savepoint (UNIQUE_SAVEPOINT_ALTER_TRIGGER);
    }

cleanup:
  if (triggers != NULL)
    {
      ml_ext_free (triggers);
    }

  return error;
}

/*
 * do_execute_trigger() - Execute the deferred activities for one or more
 *            triggers.
 *   return: Error code
 *   parser(in): Parse context
 *   statement(in): Parse tree of a execute trigger statement
 *
 * Note:
 */
int
do_execute_trigger (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  PT_NODE *speclist;
  DB_OBJLIST *triggers, *t;

  CHECK_MODIFICATION_ERROR ();

  speclist = statement->info.execute_trigger.trigger_spec_list;
  error = convert_speclist_to_objlist (&triggers, speclist);

  if (error == NO_ERROR && triggers != NULL)
    {
      for (t = triggers; t != NULL && error == NO_ERROR; t = t->next)
    {
      error = tr_execute_deferred_activities (t->op, NULL);
    }
      ml_ext_free (triggers);
    }

  return error;
}

/*
 * do_remove_trigger() - Remove the deferred activities for one or more triggers
 *   return: Error code
 *   parser(in): Parse context
 *   statement(in): Parse tree of a remove trigger statement
 *
 * Note:
 */
int
do_remove_trigger (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  PT_NODE *speclist;
  DB_OBJLIST *triggers, *t;

  CHECK_MODIFICATION_ERROR ();

  speclist = statement->info.remove_trigger.trigger_spec_list;
  error = convert_speclist_to_objlist (&triggers, speclist);

  if (error == NO_ERROR && triggers != NULL)
    {
      for (t = triggers; t != NULL && error == NO_ERROR; t = t->next)
    {
      error = tr_drop_deferred_activities (t->op, NULL);
    }

      ml_ext_free (triggers);
    }

  return error;
}

/*
 * do_rename_trigger() - Rename a trigger
 *   return: Error code
 *   parser(in): Parse context
 *   statement(in): Parse tree of a rename trigger statement
 *
 * Note:
 */
int
do_rename_trigger (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  const char *old_name, *new_name;
  DB_OBJECT *trigger;

  CHECK_MODIFICATION_ERROR ();

  old_name = statement->info.rename_trigger.old_name->info.name.original;
  new_name = statement->info.rename_trigger.new_name->info.name.original;

  trigger = tr_find_trigger (old_name);
  if (trigger == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      return error;
    }

  return tr_rename_trigger (trigger, new_name, false, false);
}

/*
 * do_set_trigger() - Set one of the trigger options
 *   return: Error code
 *   parser(in): Parse context
 *   statement(in): Parse tree of a set trigger statement
 *
 * Note:
 */
int
do_set_trigger (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  DB_VALUE src, dst;
  TP_DOMAIN_STATUS dom_status;

  db_make_null (&src);
  db_make_null (&dst);

  pt_evaluate_tree (parser, statement->info.set_trigger.val, &src, 1);
  if (pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_SEMANTIC);
      error = er_errid ();
      if (error == ER_TP_CANT_COERCE || error == ER_IT_DATA_OVERFLOW)
    {
      char buf1[MAX_DOMAIN_NAME_SIZE];
      char buf2[MAX_DOMAIN_NAME_SIZE];
      (void) tp_value_domain_name (&src, buf1, sizeof (buf1));
      (void) tp_domain_name (&tp_Integer_domain, buf2, sizeof (buf2));
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 2, buf1, buf2);
    }
    }
  else
    {
      dom_status = tp_value_coerce (&src, &dst, &tp_Integer_domain);
      if (dom_status != DOMAIN_COMPATIBLE)
    {
      error = tp_domain_status_er_set (dom_status, ARG_FILE_LINE, &src, &tp_Integer_domain);
      /* already set error */
    }
    }

  if (error == NO_ERROR)
    {
      PT_MISC_TYPE option;
      int v;

      option = statement->info.set_trigger.option;
      v = db_get_int (&dst);

      if (option == PT_TRIGGER_TRACE)
    {
      error = tr_set_trace ((bool) v);
    }
      else if (option == PT_TRIGGER_DEPTH)
    {
      error = tr_set_depth (v);
    }
    }

  /*
   * No need to clear dst, because it's either NULL or an integer at
   * this point.  src could be arbitrarily complex, and it was created
   * by pt_evaluate_tree, so we need to clear it before we leave.
   */
  db_value_clear (&src);

  return error;
}

/*
 * do_get_trigger() - Get one of the trigger option values.
 *   return: Error code
 *   parser(in): Parse context
 *   statement(in/out): Parse tree of a get trigger statement
 *
 * Note:
 */
int
do_get_trigger (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  const char *into_label;
  DB_VALUE *ins_val;
  PT_NODE *into;
  PT_MISC_TYPE option;

  /* create a value to hold the result */
  ins_val = db_value_create ();
  if (ins_val == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  option = statement->info.set_trigger.option;
  switch (option)
    {
    case PT_TRIGGER_DEPTH:
      db_make_int (ins_val, tr_get_depth ());
      break;
    case PT_TRIGGER_TRACE:
      db_make_int (ins_val, tr_get_trace ());
      break;
    default:
      db_make_null (ins_val);   /* can't happen */
      break;
    }

  statement->etc = (void *) ins_val;

  into = statement->info.get_trigger.into_var;
  if (into != NULL && into->node_type == PT_NAME && (into_label = into->info.name.original) != NULL)
    {
      /* create another DB_VALUE for the label table */
      ins_val = db_value_copy (ins_val);

      /* enter the value into the table */
      error = pt_associate_label_with_value_check_reference (into_label, ins_val);
    }

  return error;
}







/*
 * Function Group:
 * DO functions for update statements
 *
 */

typedef enum
{ NORMAL_UPDATE, UPDATE_OBJECT, ON_DUPLICATE_KEY_UPDATE } UPDATE_TYPE;

#define DB_VALUE_STACK_MAX 40

/* It is used to generate unique savepoint names */
static int update_savepoint_number = 0;

static void unlink_list (PT_NODE * list);

static QFILE_LIST_ID *get_select_list_to_update (PARSER_CONTEXT * parser, PT_NODE * from, PT_NODE * column_names,
                         PT_NODE * column_values, PT_NODE * with, PT_NODE * where,
                         PT_NODE * order_by, PT_NODE * orderby_for, PT_NODE * using_index,
                         PT_NODE * class_specs, PT_NODE * update_stmt);
static int update_object_attribute (PARSER_CONTEXT * parser, DB_OTMPL * otemplate, PT_NODE * name,
                    DB_ATTDESC * attr_desc, DB_VALUE * value);
static int update_object_tuple (PARSER_CONTEXT * parser, CLIENT_UPDATE_INFO * assigns, int assigns_count,
                CLIENT_UPDATE_CLASS_INFO * upd_classes_info, int classes_cnt,
                const int turn_off_unique_check, const int turn_off_serializable_conflict_check,
                UPDATE_TYPE update_type, bool should_delete);
static int update_object_by_oid (PARSER_CONTEXT * parser, PT_NODE * statement, UPDATE_TYPE update_type);
#if 0
static int init_update_data (PARSER_CONTEXT * parser, PT_NODE * statement, CLIENT_UPDATE_INFO ** assigns_data,
                 int *assigns_count, CLIENT_UPDATE_CLASS_INFO ** cls_data, int *cls_count,
                 DB_VALUE ** values, int *values_cnt, bool has_delete);
#endif
static int do_set_pruning_type (PARSER_CONTEXT * parser, PT_NODE * spec, CLIENT_UPDATE_CLASS_INFO * cls);
static int update_objs_for_list_file (PARSER_CONTEXT * parser, QFILE_LIST_ID * list_id, PT_NODE * statement,
                      bool savepoint_started);
static int update_class_attributes (PARSER_CONTEXT * parser, PT_NODE * statement);
static int update_at_server (PARSER_CONTEXT * parser, PT_NODE * from, PT_NODE * statement, PT_NODE ** non_null_attrs,
                 int has_uniques);
static int update_check_for_constraints (PARSER_CONTEXT * parser, int *has_unique, PT_NODE ** not_nulls,
                     const PT_NODE * statement);
static bool update_check_having_meta_attr (PARSER_CONTEXT * parser, PT_NODE * assignment);
static int update_real_class (PARSER_CONTEXT * parser, PT_NODE * statement, bool savepoint_started);
static XASL_NODE *statement_to_update_xasl (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE ** non_null_attrs);
static int is_server_update_allowed (PARSER_CONTEXT * parser, PT_NODE ** non_null_attrs, int *has_uniques,
                     int *const server_allowed, const PT_NODE * statement);
static int delete_object_tuple (DB_OBJECT * obj);
static int has_unique_constraint (DB_OBJECT * mop);

/*
 * unlink_list - Unlinks next pointer shortcut of lhs, rhs assignments
 *   return: None
 *   list(in): Node list to cut
 *
 * Note:
 */
static void
unlink_list (PT_NODE * list)
{
  PT_NODE *next;

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

/*
 * get_select_list_to_update -
 *   return: List file if success, otherwise NULL
 *   parser(in): Parser context
 *   from(in): Parse tree of an FROM class
 *   column_values(in): Column list in SELECT clause
 *   with(in): WITH clause
 *   where(in): WHERE clause
 *   order_by(in): ORDER BY clause
 *   orderby_num(in): converted from ORDER BY with LIMIT
 *   using_index(in): USING INDEX clause
 *   class_specs(in): Another class specs in FROM clause
 *
 * Note:
 */
static QFILE_LIST_ID *
get_select_list_to_update (PARSER_CONTEXT * parser, PT_NODE * from, PT_NODE * column_names, PT_NODE * column_values,
               PT_NODE * with, PT_NODE * where, PT_NODE * order_by, PT_NODE * orderby_for,
               PT_NODE * using_index, PT_NODE * class_specs, PT_NODE * update_stmt)
{
  PT_NODE *statement = NULL;
  QFILE_LIST_ID *result = NULL;
  int err = NO_ERROR;

  assert (parser->query_id == NULL_QUERY_ID);

  if (from && (from->node_type == PT_SPEC) && from->info.spec.range_var
      && ((statement =
       pt_to_upd_del_query (parser, column_names, column_values, from, with, class_specs, where, using_index,
                order_by, orderby_for, 0 /* not server update */ , S_UPDATE)) != NULL))
    {
      err = pt_copy_upddel_hints_to_select (parser, update_stmt, statement);
      if (err != NO_ERROR)
    {
      parser_free_tree (parser, statement);
      return NULL;
    }

      /* If we are updating a proxy, the select is not yet fully translated. If we are updating anything else, this is
       * a no-op. */
      statement = mq_translate (parser, statement);

      if (statement)
    {
      /* This enables authorization checking during methods in queries */
      AU_ENABLE (parser->au_save);

      assert (parser->query_id == NULL_QUERY_ID);
      if (do_select_for_ins_upd (parser, statement) < NO_ERROR)
        {
          /* query failed, an error has already been set */
          statement = NULL;
        }

      AU_DISABLE (parser->au_save);
    }
    }

  if (statement)
    {
      result = (QFILE_LIST_ID *) statement->etc;
      parser_free_tree (parser, statement);
    }

  return result;
}

/*
 * update_object_attribute -
 *   return: Error code
 *   parser(in): Parser context
 *   otemplate(in/out): Class template to be edited
 *   name(in): Parse tree of a attribute name
 *   attr_desc(in): Descriptor of attribute to update
 *   value(in): New attribute value
 *
 * Note: If db_put fails, return an error
 */
static int
update_object_attribute (PARSER_CONTEXT * parser, DB_OTMPL * otemplate, PT_NODE * name, DB_ATTDESC * attr_desc,
             DB_VALUE * value)
{
  int error = NO_ERROR;

  if (name->info.name.db_object)
    {
      error = db_is_vclass (name->info.name.db_object);
      if (error < 0)
    {
      return error;
    }
      if (error > 0)
    {
      /* this is a shared attribute of a view. this means this cannot be updated in the template for this real
       * class. Its simply done separately by a db_put. */
      error = obj_set_shared (name->info.name.db_object, name->info.name.original, value);
      return error;
    }
    }

  /* the normal case */
  error = dbt_dput_internal (otemplate, attr_desc, value);

  return error;
}

/*
 * update_object_tuple - Updates object attributes with db_values
 *   return: Error code
 *   assigns(in): array of assignments
 *   assigns_count(in): no of assignments
 *   upd_classes_info(in): array of classes info
 *   classes_cnt(in): no of classes
 *   turn_off_unique_check(in):
 *   turn_off_serializable_conflict_check(in): true, if turn off SERIALIZABLE
 *                        conflict checking
 *   update_type(in):
 *   should_delete(in):
 *
 * Note:
 */
static int
update_object_tuple (PARSER_CONTEXT * parser, CLIENT_UPDATE_INFO * assigns, int assigns_count,
             CLIENT_UPDATE_CLASS_INFO * upd_classes_info, int classes_cnt, const int turn_off_unique_check,
             const int turn_off_serializable_conflict_check, UPDATE_TYPE update_type, bool should_delete)
{
  int error = NO_ERROR;
  DB_OTMPL *otemplate = NULL;
  int idx = 0, upd_tpl_cnt = 0;
  DB_OBJECT *real_object = NULL, *object = NULL;
  SM_CLASS *smclass = NULL;
  CLIENT_UPDATE_INFO *assign = NULL;
  CLIENT_UPDATE_CLASS_INFO *cls_info = NULL;
  bool flush_del = false;
  MOP object_class_mop;

  for (idx = 0; idx < classes_cnt && error == NO_ERROR; idx++)
    {
      cls_info = &upd_classes_info[idx];

      if (DB_IS_NULL (cls_info->oid))
    {
      continue;
    }

      object = db_get_object (cls_info->oid);
      error = db_is_deleted (object);
      if (error < 0)
    {
      return error;
    }
      if (error > 0)
    {
      error = NO_ERROR;
      continue;
    }

      real_object = db_real_instance (object);
      if (real_object == NULL)
    {           /* real_object's fail */
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (error == NO_ERROR)
        {
          error = ER_GENERIC_ERROR;
        }
      return error;
    }

      /* if this is the first tuple or the class has changed to a new subclass then fetch new class */
      object_class_mop = ws_class_mop (object);
      if (cls_info->class_mop == NULL
      || (object_class_mop != NULL && ws_mop_compare (object_class_mop, cls_info->class_mop) != 0))
    {
      cls_info->class_mop = object_class_mop;

      if (object_class_mop != NULL)
        {
          error = au_fetch_class (object_class_mop, &smclass, AU_FETCH_READ, AU_SELECT);
          if (error != NO_ERROR)
        {
          return error;
        }
          cls_info->smclass = smclass;
        }
    }
      else
    {
      /* otherwise use old class */
      smclass = cls_info->smclass;
    }

      otemplate = dbt_edit_object (real_object);
      if (otemplate == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

      if (turn_off_unique_check)
    {
      obt_disable_unique_checking (otemplate);
    }

      if (turn_off_serializable_conflict_check)
    {
      obt_disable_serializable_conflict_checking (otemplate);
    }

      /* this is an update - force NOT NULL constraint check */
      otemplate->force_check_not_null = 1;

      otemplate->pruning_type = cls_info->pruning_type;

      /* If this update came from INSERT ON DUPLICATE KEY UPDATE, flush the object on updating it. */
      if (update_type == ON_DUPLICATE_KEY_UPDATE || otemplate->pruning_type != DB_NOT_PARTITIONED_CLASS)
    {
      obt_set_force_flush (otemplate);
    }

      /* iterate through class assignments and update template with new values */
      for (assign = cls_info->first_assign; assign != NULL && error == NO_ERROR; assign = assign->next)
    {
      /* if this is the first update, get the attribute descriptor */
      if (assign->attr_desc == NULL)
        {
          int is_vclass = 0;

          /* don't get descriptors for shared attrs of views */
          if (assign->upd_col_name->info.name.db_object != NULL)
        {
          is_vclass = db_is_vclass (assign->upd_col_name->info.name.db_object);

          if (is_vclass < 0)
            {
              error = is_vclass;
            }
        }
          if (!is_vclass)
        {
          error =
            db_get_attribute_descriptor (real_object, assign->upd_col_name->info.name.original, 0, 1,
                         &assign->attr_desc);
        }
        }

      if (error == NO_ERROR)
        {
          /* update tuple's template */
          error =
        update_object_attribute (parser, otemplate, assign->upd_col_name, assign->attr_desc, assign->db_val);

          /* clear not constant values */
          if (!assign->is_const)
        {
          db_value_clear (assign->db_val);
        }
        }
    }

      if (error != NO_ERROR)
    {
      /* abort if an error has occurred */
      (void) dbt_abort_object (otemplate);
    }
      else
    {
      /* update tuple with new values */
      object = dbt_finish_object (otemplate);
      if (object == NULL)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
          (void) dbt_abort_object (otemplate);
          return error;
        }
      else
        {
          /* check condition for 'with check option' */
          error = mq_evaluate_check_option (parser,
                        (cls_info->check_where != NULL
                         ? cls_info->check_where->info.check_option.expr : NULL), object,
                        cls_info->spec->info.spec.flat_entity_list);
        }
    }

      /* handle delete only after update to give a chance to triggers */
      if (should_delete && error == NO_ERROR)
    {
      error = locator_flush_instance (object);
      if (error != NO_ERROR)
        {
          continue;
        }
      flush_del = has_unique_constraint (object);
      error = delete_object_tuple (object);
      if (error == ER_HEAP_UNKNOWN_OBJECT && do_Trigger_involved)
        {
          er_clear ();
          error = NO_ERROR;
          continue;
        }
      if (flush_del && error == NO_ERROR)
        {
          error = locator_flush_instance (object);
        }
    }
      upd_tpl_cnt++;
    }

  return error == NO_ERROR ? upd_tpl_cnt : error;
}

/*
 * update_object_by_oid - Updates attributes of object by oid
 *   return: 1 if success, otherwise returns error code
 *   parser(in): Parser context
 *   statement(in): Parse tree of a update statement
 *   update_type(in): denote whether the update comes from normal update stmt,
 *            update object stmt or insert on duplicate key update stmt.
 *
 * Note:
 */
static int
update_object_by_oid (PARSER_CONTEXT * parser, PT_NODE * statement, UPDATE_TYPE update_type)
{
  int error = NO_ERROR;
  DB_OBJECT *oid = statement->info.update.object;
  int i = 0;
  PT_NODE *node = NULL;
  int vals_cnt = 0;
  PT_NODE *class_;
  PT_NODE *lhs;

  if (!statement->info.update.spec || !(class_ = statement->info.update.spec->info.spec.flat_entity_list)
      || !(class_->info.name.db_object) || statement->info.update.spec->next != NULL)
    {
      PT_INTERNAL_ERROR (parser, "update");
      return ER_GENERIC_ERROR;
    }

  /* fetch classes that will be updated */
  node = statement->info.update.spec;
  while (node)
    {
      if (node->info.spec.flag & PT_SPEC_FLAG_UPDATE)
    {
      if (!locator_fetch_class (node->info.spec.flat_entity_list->info.name.db_object, DB_FETCH_CLREAD_INSTWRITE))
        {
          assert (er_errid () != NO_ERROR);
          return er_errid ();
        }
    }

      node = node->next;
    }

  /* get first argument of first assignment */
  lhs = statement->info.update.assignment->info.expr.arg1;
  if (PT_IS_N_COLUMN_UPDATE_EXPR (lhs))
    {
      lhs = lhs->info.expr.arg1;
    }

  if (lhs->info.name.meta_class == PT_META_ATTR)
    {
      /* if left argument of first assignment is an attribute then all other assignments are to class attributes */
      error = update_class_attributes (parser, statement);
    }
  else
    {
      /* update object */
      int assigns_count = 0, upd_cls_cnt = 0, multi_assign_cnt = 0;
      CLIENT_UPDATE_INFO *assigns = NULL;
      CLIENT_UPDATE_CLASS_INFO *cls_info = NULL;
      DB_VALUE *dbvals = NULL;
      PT_ASSIGNMENTS_HELPER ea;
      PT_NODE *rhs = NULL, *lhs = NULL;

      /* Before we start evaluating assignments, we need to first lock the object. Assignments may depend on current
       * object values which cannot be modified by others. */
      error = obj_inst_lock (oid, 1);
      if (error != NO_ERROR)
    {
      PT_INTERNAL_ERROR (parser, "update_object_by_oid failed to lock object.");
      return error;
    }

      /* load structures for update */
      error =
    init_update_data (parser, statement, &assigns, &assigns_count, &cls_info, &upd_cls_cnt, &dbvals, &vals_cnt,
              false);

      if (error == NO_ERROR)
    {
      db_make_object (&dbvals[0], oid);

      /* iterate through assignments and evaluate right side of each assignment */
      i = 0;
      pt_init_assignments_helper (parser, &ea, statement->info.update.assignment);
      while (pt_get_next_assignment (&ea) && error == NO_ERROR)
        {
          rhs = ea.rhs;
          lhs = ea.lhs;
          multi_assign_cnt = 1;
          /* for multi-column assignments with common right side count number of attributes to assign to */
          if (ea.is_n_column)
        {
          while (pt_get_next_assignment (&ea) && rhs == ea.rhs)
            {
              multi_assign_cnt++;
            }
        }

          error =
        mq_evaluate_expression_having_serial (parser, rhs, assigns[i].db_val, multi_assign_cnt, oid,
                              lhs->info.name.spec_id);
          i += multi_assign_cnt;
        }

      /* update tuple */
      if (error >= NO_ERROR)
        {
          error =
        update_object_tuple (parser, assigns, assigns_count, cls_info, upd_cls_cnt, 0, 0, update_type, false);
        }

    }

      /* free assignments array */
      if (assigns != NULL)
    {
      /* free attribute descriptors */
      for (i = assigns_count - 1; i >= 0; i--)
        {
          if (assigns[i].attr_desc)
        {
          db_free_attribute_descriptor (assigns[i].attr_desc);
        }
        }
      db_private_free (NULL, assigns);
    }

      /* free classes information */
      if (cls_info != NULL)
    {
      db_private_free (NULL, cls_info);
    }

      /* free dbvals array */
      if (dbvals != NULL)
    {
      db_private_free (NULL, dbvals);
    }
    }

  if (error < NO_ERROR)
    return error;
  else
    return 1;           /* we successfully updated 1 object */
}

/*
 * do_set_pruning_type () - set pruning type for a spec
 * return : error code or NO_ERROR
 * parser (in)  : parser context
 * spec (in)    : spec
 * cls (in) : update class info
 */
static int
do_set_pruning_type (PARSER_CONTEXT * parser, PT_NODE * spec, CLIENT_UPDATE_CLASS_INFO * cls)
{
  int error = NO_ERROR;
  MOP class_mop = NULL;
  PT_NODE *derived = NULL;
  if (cls == NULL || spec == NULL)
    {
      return NO_ERROR;
    }
  if (spec->node_type != PT_SPEC)
    {
      return NO_ERROR;
    }
  if (PT_SPEC_IS_ENTITY (spec))
    {
      if (spec->info.spec.entity_name->node_type == PT_NAME)
    {
      class_mop = spec->info.spec.entity_name->info.name.db_object;
      if (class_mop == NULL)
        {
          PT_ERROR (parser, spec, "Generic error");
          return ER_FAILED;
        }
      error = sm_partitioned_class_type (class_mop, &cls->pruning_type, NULL, NULL);
      return error;
    }
      else if (spec->info.spec.entity_name->node_type == PT_SPEC)
    {
      /* (classA, classB) specification. We do not allow partitions in this context */
      PT_NODE *node = spec->info.spec.entity_name;
      while (node)
        {
          error = do_set_pruning_type (parser, node, cls);
          if (cls->pruning_type == DB_PARTITION_CLASS)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_NOT_ALLOWED_ACCESS_TO_PARTITION,
                  node->info.spec.entity_name->info.name.original);
          return ER_FAILED;
        }
          node = node->next;
        }
      return NO_ERROR;
    }
    }

  if (PT_SPEC_IS_CTE (spec))
    {
      PT_ERROR (parser, spec, "CTE not handled");
      return ER_FAILED;
    }

  /* We're in the context of a table update/insert etc. This is possible only if the derived table is a SELECT and has
   * only one updated class */
  derived = spec->info.spec.derived_table;
  error = NO_ERROR;
  if (derived->node_type == PT_SELECT)
    {
      for (spec = derived->info.query.q.select.from; spec != NULL; spec = spec->next)
    {
      if (error == NO_ERROR && (spec->info.spec.flag & PT_SPEC_FLAG_UPDATE))
        {
          error = do_set_pruning_type (parser, spec, cls);
        }
    }
    }
  else if (derived->node_type == PT_UNION)
    {
      if (derived->info.query.q.union_.arg1 != NULL && derived->info.query.q.union_.arg1->node_type == PT_SELECT)
    {
      for (spec = derived->info.query.q.union_.arg1->info.query.q.select.from; spec != NULL; spec = spec->next)
        {
          if (error == NO_ERROR && (spec->info.spec.flag & PT_SPEC_FLAG_UPDATE))
        {
          error = do_set_pruning_type (parser, spec, cls);
        }
        }
    }

      if (derived->info.query.q.union_.arg2 != NULL && derived->info.query.q.union_.arg2->node_type == PT_SELECT)
    {
      for (spec = derived->info.query.q.union_.arg2->info.query.q.select.from; spec != NULL; spec = spec->next)
        {
          if (error == NO_ERROR && (spec->info.spec.flag & PT_SPEC_FLAG_UPDATE))
        {
          error = do_set_pruning_type (parser, spec, cls);
        }
        }
    }
    }
  else
    {
      PT_ERROR (parser, spec, "Generic error");
      return ER_FAILED;
    }

  return error;
}

/*
 * init_update_data () - init update data structures
 *   return: NO_ERROR or error code
 *   parser(in): Parser context
 *   assigns_data(out): address of a pointer variable that will receive the
 *             array for assignments info. This array must be
 *             released by the caller
 *   assigns_no(out): address of a int variable that will receive number of
 *            assignments
 *   cls_data(out): address of a pointer that will receive information about
 *          classes that will be updated
 *   cls_count(out): address of a integer variable that will receive number of
 *           classes that will be updated
 *   values(out): address of a pointer that will receive an array of DB_VALUE
 *        that represents runtime computed values and constants.
 *        This array is referenced by elements of assigns_data.
 *   values_cnt(out): number of classes OIDs + values computed at
 *             runtime for assignments.
 *   has_delete(in): update/delete
 *
 * Note:
 */
int
init_update_data (PARSER_CONTEXT * parser, PT_NODE * statement, CLIENT_UPDATE_INFO ** assigns_data, int *assigns_count,
          CLIENT_UPDATE_CLASS_INFO ** cls_data, int *cls_count, DB_VALUE ** values, int *values_cnt,
          bool has_delete)
{
  int error = NO_ERROR;
  int assign_cnt = 0, upd_cls_cnt = 0, vals_cnt = 0, idx, idx2, idx3, i;
  PT_ASSIGNMENTS_HELPER ea;
  PT_NODE *node = NULL, *assignments, *spec, *class_spec, *check_where;
  DB_VALUE *dbvals = NULL;
  CLIENT_UPDATE_CLASS_INFO *cls_info = NULL, *cls_info_tmp = NULL;
  CLIENT_UPDATE_INFO *assigns = NULL, *assign = NULL, *assign2 = NULL;

  assign_cnt = vals_cnt = 0;
  assignments =
    statement->node_type == PT_MERGE ? statement->info.merge.update.assignment : statement->info.update.assignment;
  spec = statement->node_type == PT_MERGE ? statement->info.merge.into : statement->info.update.spec;
  class_spec = statement->node_type == PT_MERGE ? NULL : statement->info.update.class_specs;
  check_where =
    statement->node_type == PT_MERGE ? statement->info.merge.check_where : statement->info.update.check_where;

  pt_init_assignments_helper (parser, &ea, assignments);
  while (pt_get_next_assignment (&ea))
    {
      if (!ea.is_rhs_const)
    {
      /* count values that are not constants */
      vals_cnt++;
    }
      /* count number of assignments */
      assign_cnt++;
    }

  /* allocate memory for assignment structures */
  assigns = (CLIENT_UPDATE_INFO *) db_private_alloc (NULL, assign_cnt * sizeof (CLIENT_UPDATE_INFO));
  if (assigns == NULL)
    {
      error = ER_REGU_NO_SPACE;
      goto error_return;
    }
  memset (assigns, 0, assign_cnt * sizeof (CLIENT_UPDATE_INFO));

  node = spec;
  while (node)
    {
      if (node->info.spec.flag & PT_SPEC_FLAG_UPDATE)
    {
      /* count classes that will be updated */
      upd_cls_cnt++;
    }

      node = node->next;
    }

  node = class_spec;
  while (node)
    {
      if (node->info.spec.flag & PT_SPEC_FLAG_UPDATE)
    {
      /* count classes that will be updated */
      upd_cls_cnt++;
    }
      node = node->next;
    }

  /* allocate array of classes information structures */
  cls_info = (CLIENT_UPDATE_CLASS_INFO *) db_private_alloc (NULL, upd_cls_cnt * sizeof (CLIENT_UPDATE_CLASS_INFO));
  if (cls_info == NULL)
    {
      error = ER_REGU_NO_SPACE;
      goto error_return;
    }
  memset (cls_info, 0, upd_cls_cnt * sizeof (CLIENT_UPDATE_CLASS_INFO));

  /* add number of class oid's */
  vals_cnt += upd_cls_cnt;
  vals_cnt += has_delete;
  /* allocate array of DB_VALUE's. The length of the array must be equal to that of select statement's list */
  dbvals = (DB_VALUE *) db_private_alloc (NULL, (assign_cnt + upd_cls_cnt + has_delete) * sizeof (DB_VALUE));
  if (dbvals == NULL)
    {
      error = ER_REGU_NO_SPACE;
      goto error_return;
    }

  for (i = 0; i < assign_cnt + upd_cls_cnt + has_delete; i++)
    {
      db_make_null (&dbvals[i]);
    }

  /* initialize classes info array */
  idx = 0;
  node = spec;
  while (node)
    {
      if (node->info.spec.flag & PT_SPEC_FLAG_UPDATE)
    {
      PT_NODE *save = check_where;

      cls_info_tmp = &cls_info[idx++];
      cls_info_tmp->spec = node;
      cls_info_tmp->first_assign = NULL;
      cls_info_tmp->class_mop = NULL;
      cls_info_tmp->pruning_type = DB_NOT_PARTITIONED_CLASS;
      error = do_set_pruning_type (parser, node, cls_info_tmp);
      if (error != NO_ERROR)
        {
          goto error_return;
        }
      /* condition to check for 'with check option' option */
      while (check_where != NULL && check_where->info.check_option.spec_id != node->info.spec.id)
        {
          check_where = check_where->next;
        }
      cls_info_tmp->check_where = check_where;

      check_where = save;
    }

      node = node->next;
    }

  /* initialize classes info array */
  node = class_spec;
  while (node)
    {
      if (node->info.spec.flag & PT_SPEC_FLAG_UPDATE)
    {
      PT_NODE *save = check_where;

      cls_info_tmp = &cls_info[idx++];
      cls_info_tmp->spec = node;
      cls_info_tmp->first_assign = NULL;
      cls_info_tmp->class_mop = NULL;
      cls_info_tmp->pruning_type = DB_NOT_PARTITIONED_CLASS;
      error = do_set_pruning_type (parser, node, cls_info_tmp);
      if (error != NO_ERROR)
        {
          goto error_return;
        }

      /* condition to check for 'with check option' option */
      while (check_where != NULL && check_where->info.check_option.spec_id != node->info.spec.id)
        {
          check_where = check_where->next;
        }
      cls_info_tmp->check_where = check_where;

      check_where = save;
    }

      node = node->next;
    }

  /* Fill assignment structures */
  idx = 0;
  pt_init_assignments_helper (parser, &ea, assignments);
  for (idx3 = 1, assign = assigns; pt_get_next_assignment (&ea); assign++)
    {
      assign->attr_desc = NULL;
      assign->upd_col_name = ea.lhs;
      /* Distribution of dbvals array. The array must match the select list of the generated select statement: first
       * upd_cls_cnt elements must be associated with OID representing tuple from a class, followed by values that must
       * be calculated at runtime for assignment and then by constants */
      if (ea.is_rhs_const)
    {
      /* constants */
      assign->db_val = &dbvals[assign_cnt + upd_cls_cnt + has_delete - idx3++];
      *assign->db_val = *pt_value_to_db (parser, ea.rhs);
      assign->is_const = true;
    }
      else
    {
      /* not constants */
      assign->db_val = &dbvals[upd_cls_cnt + has_delete + idx++];
      assign->is_const = false;
    }

      for (idx2 = 0; idx2 < upd_cls_cnt; idx2++)
    {
      if (cls_info[idx2].spec->info.spec.id == ea.lhs->info.name.spec_id)
        {
          /* OIDs are in reverse order */
          cls_info[idx2].oid = &dbvals[upd_cls_cnt - idx2 - 1];
          /* attach class information to assignment */
          assign->cls_info = &cls_info[idx2];
          /* link assignment to its class info */
          if (cls_info[idx2].first_assign)
        {
          assign2 = cls_info[idx2].first_assign;
          while (assign2->next)
            {
              assign2 = assign2->next;
            }
          assign2->next = assign;
        }
          else
        {
          cls_info[idx2].first_assign = assign;
        }
          assign->next = NULL;
          break;
        }
    }
    }

  /* output computed data */
  *assigns_data = assigns;
  *assigns_count = assign_cnt;
  *cls_data = cls_info;
  *cls_count = upd_cls_cnt;
  *values = dbvals;
  *values_cnt = vals_cnt;

  return error;

error_return:
  /* free class information array */
  if (cls_info)
    {
      db_private_free (NULL, cls_info);
    }

  /* free assignments information */
  if (assigns != NULL)
    {
      /* free attribute descriptors */
      for (idx = 0; idx < assign_cnt; idx++)
    {
      assign = &assigns[idx];
      if (assign->attr_desc)
        {
          db_free_attribute_descriptor (assign->attr_desc);
        }
    }
      db_private_free (NULL, assigns);
    }

  /* free dbvals array */
  if (dbvals != NULL)
    {
      db_private_free (NULL, dbvals);
    }

  return error;
}

/*
 * update_objs_for_list_file - Updates oid attributes for every oid
 *              in a list file
 *   return: Number of affected objects if success, otherwise an error code
 *   parser(in): Parser context
 *   list_id(in): A list file of oid's and values
 *   statement(in): update statement
 *   savepoint_started(in): true, if savepoint started
 *
 * Note:
 */
static int
update_objs_for_list_file (PARSER_CONTEXT * parser, QFILE_LIST_ID * list_id, PT_NODE * statement,
               bool savepoint_started)
{
  int error = NO_ERROR;
  int idx = 0, count = 0, assign_count = 0;
  int upd_cls_cnt = 0, vals_cnt = 0;
  CLIENT_UPDATE_INFO *assigns = NULL, *assign = NULL;
  CLIENT_UPDATE_CLASS_INFO *cls_info = NULL;
  int turn_off_unique_check;
  int turn_off_server_serializable_conflict_check;
  CURSOR_ID cursor_id;
  DB_VALUE *dbvals = NULL;
  const char *savepoint_name = NULL;
  int cursor_status;
  PT_NODE *check_where;
  bool has_unique, has_trigger;
  bool has_delete, should_delete = false;
  bool client_check_serializable_conflict = false;

  if (list_id == NULL || statement == NULL)
    {
      er_set (ER_ERROR_SEVERITY, __FILE__, __LINE__, ER_REGU_SYSTEM, 0);
      error = ER_REGU_SYSTEM;
      goto done;
    }

  check_where =
    statement->node_type == PT_MERGE ? statement->info.merge.check_where : statement->info.update.check_where;
  has_unique =
    statement->node_type ==
    PT_MERGE ? (statement->info.merge.flags & PT_MERGE_INFO_HAS_UNIQUE) : statement->info.update.has_unique;
  /* For merge stmt, the savepoint has already been added in do_merge() or do_execute_merge(). we do not need to check
   * the trigger any more. */
  has_trigger = (statement->node_type == PT_UPDATE && statement->info.update.has_trigger);
  has_delete = (statement->node_type == PT_MERGE && statement->info.merge.update.has_delete);

  /* load data in update structures */
  error =
    init_update_data (parser, statement, &assigns, &assign_count, &cls_info, &upd_cls_cnt, &dbvals, &vals_cnt,
              has_delete);
  if (error != NO_ERROR)
    {
      goto done;
    }

  /* if the list file contains more than 1 object we need to savepoint the statement to guarantee statement atomicity. */
  if (list_id->tuple_cnt > 1 || check_where || has_unique || has_trigger || TM_TRAN_ISOLATION () >= TRAN_REP_READ)
    {
      if (savepoint_started == false)
    {
      savepoint_name = mq_generate_name (parser, "UusP", &update_savepoint_number);
      error = tran_system_savepoint (savepoint_name);
      if (error != NO_ERROR)
        {
          goto done;
        }
    }
    }

  /* 'turn_off_unique_check' is used when call update_object_tuple(). */
  if (list_id->tuple_cnt == 1 && upd_cls_cnt == 1)
    {
      /* Instance level uniqueness and SERIALIZABLE conflict checking is performed on the server when a new single row
       * is inserted. */
      turn_off_unique_check = 0;
      turn_off_server_serializable_conflict_check = 0;
    }
  else
    {
      /* list_id->tuple_cnt > 1 : multiple row update Statement level uniqueness checking and SERIALIZABLE conflict is
       * performed on the client */
      turn_off_unique_check = 1;
      turn_off_server_serializable_conflict_check = 1;
    }

  /* open cursor */
  if (!cursor_open (&cursor_id, list_id, false, false))
    {
      error = ER_GENERIC_ERROR;
      if (savepoint_name && (error != ER_LK_UNILATERALLY_ABORTED))
    {
      (void) tran_abort_upto_system_savepoint (savepoint_name);
    }
      goto done;
    }
  cursor_id.query_id = parser->query_id;

  /* set prefetching lock mode to WRITE access since we'll be updating all the objects in the list file. */
  (void) cursor_set_prefetch_lock_mode (&cursor_id, DB_FETCH_WRITE);

  cursor_status = cursor_next_tuple (&cursor_id);

  while (cursor_status == DB_CURSOR_SUCCESS)
    {
      /* read OIDs and runtime computed values */
      if (cursor_get_tuple_value_list (&cursor_id, vals_cnt, dbvals) != NO_ERROR)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      cursor_close (&cursor_id);
      if (savepoint_name && (error != ER_LK_UNILATERALLY_ABORTED))
        {
          (void) tran_abort_upto_system_savepoint (savepoint_name);
        }
      goto done;
    }

      if (has_delete)
    {
      /* We may get NULL as an expr value. See pt_to_merge_update_query(...). */
      if (DB_IS_NULL (&dbvals[upd_cls_cnt]))
        {
          should_delete = false;
        }
      else
        {
          should_delete = db_get_int (&dbvals[upd_cls_cnt]);
        }
    }

      /* perform update for current tuples */
      error =
    update_object_tuple (parser, assigns, assign_count, cls_info, upd_cls_cnt, turn_off_unique_check,
                 turn_off_server_serializable_conflict_check, NORMAL_UPDATE, should_delete);

      /* close cursor and restore to savepoint in case of error */
      if (error < NO_ERROR)
    {
#if 0               /* TODO */
      /* mq_evaluate_check_option () do not er_set () */
      assert (er_errid () != NO_ERROR);
#endif
      error = er_errid ();
      cursor_close (&cursor_id);
      if (savepoint_name && (error != ER_LK_UNILATERALLY_ABORTED))
        {
          (void) tran_abort_upto_system_savepoint (savepoint_name);
        }
      goto done;
    }

      count += error;       /* number of objects affected. Incorrect for multi-table update !!! */
      cursor_status = cursor_next_tuple (&cursor_id);
    }

  /* close cursor and restore to savepoint in case of error */
  if (cursor_status != DB_CURSOR_END)
    {
      error = ER_GENERIC_ERROR;
      cursor_close (&cursor_id);
      if (savepoint_name && (error != ER_LK_UNILATERALLY_ABORTED))
    {
      (void) tran_abort_upto_system_savepoint (savepoint_name);
    }
      goto done;
    }
  cursor_close (&cursor_id);

  if (TM_TRAN_ISOLATION () >= TRAN_REP_READ && turn_off_server_serializable_conflict_check)
    {
      /* activate SERIALIZABLE conflict checks */
      client_check_serializable_conflict = true;
    }

  /* check uniques or SERIALIZABLE conflicts if not already checked */
  if (has_unique || client_check_serializable_conflict)
    {
      /* need to check unique or SERIALIZABLE conflicts */
      for (idx = upd_cls_cnt - 1; idx >= 0; idx--)
    {
      if (!(cls_info[idx].spec->info.spec.flag & PT_SPEC_FLAG_HAS_UNIQUE) && !client_check_serializable_conflict)
        {
          /* if not unique and don't want to check SERIALIZABLE conflict */
          continue;
        }
      error = sm_flush_for_multi_update (cls_info[idx].spec->info.spec.flat_entity_list->info.name.db_object);
      /* if error and a savepoint was created, rollback to savepoint. No need to rollback if the TM aborted the
       * transaction itself. */
      if ((error < NO_ERROR) && savepoint_name && (error != ER_LK_UNILATERALLY_ABORTED))
        {
          (void) tran_abort_upto_system_savepoint (savepoint_name);
          break;
        }
    }
    }

done:
  /* free classes information array */
  if (cls_info)
    {
      db_private_free (NULL, cls_info);
    }

  /* free assignments information */
  if (assigns != NULL)
    {
      /* free attributes descriptors */
      for (idx = 0; idx < assign_count; idx++)
    {
      assign = &assigns[idx];
      if (assign->attr_desc)
        {
          db_free_attribute_descriptor (assign->attr_desc);
        }
    }
      db_private_free (NULL, assigns);
    }

  /* free values array */
  if (dbvals != NULL)
    {
      db_private_free (NULL, dbvals);
    }

  if (error >= NO_ERROR)
    {
      return count;
    }
  else
    {
      return error;
    }
}

/*
 * update_class_attributes - Returns corresponding lists of names and expressions
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in): update statement
 *
 * Note:
 */
static int
update_class_attributes (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  DB_OTMPL *otemplate = NULL;
  PT_NODE *rhs = NULL, *assignments = NULL;
  PT_ASSIGNMENTS_HELPER ea;
  int idx = 0, assigns_count = 0;
  int upd_cls_cnt = 0, vals_cnt = 0, multi_assign_cnt = 0;
  CLIENT_UPDATE_INFO *assigns = NULL, *assign = NULL;
  CLIENT_UPDATE_CLASS_INFO *cls_info = NULL, *cls = NULL;
  DB_VALUE *dbvals = NULL;

  assignments =
    statement->node_type == PT_MERGE ? statement->info.merge.update.assignment : statement->info.update.assignment;

  /* load data for update */
  error =
    init_update_data (parser, statement, &assigns, &assigns_count, &cls_info, &upd_cls_cnt, &dbvals, &vals_cnt, false);
  if (error == NO_ERROR)
    {
      /* evaluate values for assignment */
      pt_init_assignments_helper (parser, &ea, assignments);
      for (idx = 0; idx < assigns_count && error == NO_ERROR; idx += multi_assign_cnt)
    {
      assign = &assigns[idx];
      cls = assign->cls_info;

      pt_get_next_assignment (&ea);
      rhs = ea.rhs;
      multi_assign_cnt = 1;
      if (ea.is_n_column)
        {
          while (pt_get_next_assignment (&ea) && rhs == ea.rhs)
        {
          multi_assign_cnt++;
        }
        }

      pt_evaluate_tree (parser, rhs, assign->db_val, multi_assign_cnt);
      if (pt_has_error (parser))
        {
          error = ER_GENERIC_ERROR;
        }
    }
    }

  /* execute assignments */
  if (error == NO_ERROR)
    {
      for (idx = 0; idx < upd_cls_cnt && error == NO_ERROR; idx++)
    {
      cls = &cls_info[idx];

      otemplate = dbt_edit_object (cls->spec->info.spec.flat_entity_list->info.name.db_object);
      for (assign = cls->first_assign; assign != NULL && error == NO_ERROR; assign = assign->next)
        {
          error = dbt_put_internal (otemplate, assign->upd_col_name->info.name.original, assign->db_val);
        }
      if (error == NO_ERROR && dbt_finish_object (otemplate) == NULL)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
          (void) dbt_abort_object (otemplate);
        }
    }
    }

  /* free assignments array */
  if (assigns != NULL)
    {
      /* free attributes descriptors */
      for (idx = assigns_count - 1; idx >= 0; idx--)
    {
      if (assigns[idx].attr_desc)
        {
          db_free_attribute_descriptor (assigns[idx].attr_desc);
        }
    }
      db_private_free (NULL, assigns);
    }

  /* free classes info array */
  if (cls_info != NULL)
    {
      db_private_free (NULL, cls_info);
    }

  /* free values array */
  if (dbvals != NULL)
    {
      db_private_free (NULL, dbvals);
    }

  return error;
}

/*
 * statement_to_update_xasl - Converts an update parse tree to
 *                an XASL graph for an update
 *   parser(in): Parser context
 *   statement(in): Parse tree of a update statement
 *   non_null_attrs(in):
 *
 * Note:
 */
static XASL_NODE *
statement_to_update_xasl (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE ** non_null_attrs)
{
  return pt_to_update_xasl (parser, statement, non_null_attrs);
}

/*
 * init_compile_context() -
 *
 *   parser (in/out):
 *
 */
static void
init_compile_context (PARSER_CONTEXT * parser)
{
  memset (&parser->context, 0x00, sizeof (COMPILE_CONTEXT));
  parser->context.is_xasl_pinned_reference = (bool) parser->flag.is_xasl_pinned_reference;
  parser->context.recompile_xasl_pinned = (bool) parser->flag.recompile_xasl_pinned;
}

/*
 * init_xasl_stream() - init XASL_STREAM
 *
 *   stream (in): initialized parameter
 *
 */
static void
init_xasl_stream (XASL_STREAM * stream)
{
  memset (stream, 0x00, sizeof (XASL_STREAM));
}

/*
 * update_at_server - Build an xasl tree for a server update and execute it
 *   return: Tuple count if success, otherwise an error code
 *   parser(in): Parser context
 *   from(in): Class spec to update
 *   statement(in): Parse tree of a update statement
 *   non_null_attrs(in):
 *   has_uniques(in):
 *
 * Note:
 *  The xasl tree has an UPDATE_PROC node as the top node and
 *  a BUILDLIST_PROC as its aptr.  The BUILDLIST_PROC selects the
 *  instance OID and any update attribute expression values.
 *  The UPDATE_PROC node scans the BUILDLIST_PROC results.
 *  The UPDATE_PROC node contains the attribute ID's and values
 *  for update constants.  The server executes the aptr and then
 *  for each instance selected, updates it with the attribute expression
 *  values and constants.  The result information is sent back to the
 *  client as a list file without any pages.  The list file tuple count
 *  is used as the return value from this routine.
 *
 *  The instances for the class are flushed from the client before the
 *  update is executed.  If any instances are updated, the instances are
 *  decached from the client after the update is executed.
 *
 *  It is assumed that class attributes and regular attributes
 *  are not mixed in the same update statement.
 */
static int
update_at_server (PARSER_CONTEXT * parser, PT_NODE * from, PT_NODE * statement, PT_NODE ** non_null_attrs,
          int has_uniques)
{
  int error = NO_ERROR;
  int i;
  XASL_NODE *xasl = NULL;
  INT64 count = 0;
  QUERY_ID query_id_self = parser->query_id;
  QFILE_LIST_ID *list_id = NULL;
  PT_NODE *cl_name_node = NULL, *spec = NULL;

  XASL_STREAM stream;

  assert (parser->query_id == NULL_QUERY_ID);

  init_xasl_stream (&stream);

  /* mark the beginning of another level of xasl packing */
  pt_enter_packing_buf ();

  xasl = pt_to_update_xasl (parser, statement, non_null_attrs);

  if (xasl)
    {
      UPDATE_PROC_NODE *update = &xasl->proc.update;

      error = xts_map_xasl_to_stream (xasl, &stream);
      if (error != NO_ERROR)
    {
      PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
    }

      for (i = 0; i < update->num_assigns; i++)
    {
      if (update->assigns[i].constant)
        {
          pr_clear_value (update->assigns[i].constant);
        }
    }
    }
  else
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
    }

  if (error == NO_ERROR)
    {
      int au_save;
      QUERY_FLAG query_flag;

      query_flag = DEFAULT_EXEC_MODE;

      if (parser->flag.is_auto_commit)
    {
      query_flag |= TRAN_AUTO_COMMIT;
    }

      AU_SAVE_AND_ENABLE (au_save); /* this insures authorization checking for method */

      error =
    prepare_and_execute_query (stream.buffer, stream.buffer_size, &parser->query_id,
                   parser->host_var_count + parser->auto_param_count, parser->host_variables, &list_id,
                   query_flag);
      AU_RESTORE (au_save);
    }

  /* free 'stream' that is allocated inside of xts_map_xasl_to_stream() */
  if (stream.buffer)
    {
      free_and_init (stream.buffer);
    }

  if (list_id)
    {
      count = list_id->tuple_cnt;
      if (count > 0)
    {
      spec = statement->info.update.spec;
      while (spec)
        {
          for (cl_name_node = spec->info.spec.flat_entity_list; cl_name_node && error == NO_ERROR;
           cl_name_node = cl_name_node->next)
        {
          if (statement->flag.use_auto_commit && tran_was_latest_query_committed ())
            {
              /* Nothing to flush. Avoids flush, since may fetch the class. */
              error = sm_decache_instances_after_query_executed_with_commit (cl_name_node->info.name.db_object);
            }
          else
            {
              error = sm_flush_and_decache_objects (cl_name_node->info.name.db_object, true);
            }

        }
          spec = spec->next;
        }
    }
      cursor_free_self_list_id (list_id);
    }
  pt_end_query (parser, query_id_self);

  /* mark the end of another level of xasl packing */
  pt_exit_packing_buf ();

  if (error >= NO_ERROR)
    {
      return count;
    }
  else
    {
      return error;
    }
}

/*
 * update_check_for_constraints - Determine whether attributes of the target
 *                classes have UNIQUE and/or NOT NULL
 *                constraints, and return a list of NOT NULL
 *                attributes if exist
 *   return: Error code
 *   parser(in): Parser context
 *   has_unique(out): Indicator representing there is UNIQUE constraint, 1 or 0
 *   not_nulls(out): A list of pointers to NOT NULL attributes, or NULL
 *   statement(in):  Parse tree of an UPDATE or MERGE statement
 *
 * Note:
 */
static int
update_check_for_constraints (PARSER_CONTEXT * parser, int *has_unique, PT_NODE ** not_nulls, const PT_NODE * statement)
{
  int error = NO_ERROR;
  PT_NODE *lhs = NULL, *att = NULL, *pointer = NULL, *spec = NULL;
  PT_NODE *assignment = NULL;
  DB_OBJECT *class_obj = NULL;

  assignment =
    statement->node_type == PT_MERGE ? statement->info.merge.update.assignment : statement->info.update.assignment;

  *has_unique = 0;
  *not_nulls = NULL;

  for (; assignment; assignment = assignment->next)
    {
      lhs = assignment->info.expr.arg1;
      if (lhs->node_type == PT_NAME)
    {
      att = lhs;
    }
      else if (PT_IS_N_COLUMN_UPDATE_EXPR (lhs))
    {
      att = lhs->info.expr.arg1;
    }
      else
    {
      /* bullet proofing, should not get here */
#if defined(CUBRID_DEBUG)
      fprintf (stdout, "system error detected in %s, line %d.\n", __FILE__, __LINE__);
#endif
      error = ER_GENERIC_ERROR;
      goto exit_on_error;
    }

      for (; att; att = att->next)
    {
      if (att->node_type != PT_NAME)
        {
          /* bullet proofing, should not get here */
#if defined(CUBRID_DEBUG)
          fprintf (stdout, "system error detected in %s, line %d.\n", __FILE__, __LINE__);
#endif
          error = ER_GENERIC_ERROR;
          goto exit_on_error;
        }

      spec = pt_find_spec_in_statement (parser, statement, att);
      /* no need to check for dblink spec */
      if (spec->info.spec.remote_server_name
          && spec->info.spec.remote_server_name->node_type == PT_DBLINK_TABLE_DML)
        {
          continue;
        }

      if (spec == NULL || (class_obj = spec->info.spec.flat_entity_list->info.name.db_object) == NULL)
        {
          error = ER_GENERIC_ERROR;
          goto exit_on_error;
        }

      if ((spec->info.spec.flag & (PT_SPEC_FLAG_HAS_UNIQUE | PT_SPEC_FLAG_DOESNT_HAVE_UNIQUE)) == 0)
        {
          bool has_unique_temp = false;
          bool check_subclasses = (spec->info.spec.only_all == PT_ALL);

          error = sm_class_has_unique_constraint (NULL, class_obj, check_subclasses, &has_unique_temp);
          if (error == NO_ERROR)
        {
          if (has_unique_temp)
            {
              *has_unique = 1;
              spec->info.spec.flag = (PT_SPEC_FLAG) (spec->info.spec.flag | PT_SPEC_FLAG_HAS_UNIQUE);
            }
          else
            {
              spec->info.spec.flag = (PT_SPEC_FLAG) (spec->info.spec.flag | PT_SPEC_FLAG_DOESNT_HAVE_UNIQUE);
            }
        }
          else
        {
          goto exit_on_error;
        }

        }
      if (sm_att_constrained (class_obj, att->info.name.original, SM_ATTFLAG_NON_NULL))
        {
          pointer = pt_point (parser, att);
          if (pointer == NULL)
        {
          PT_ERRORm (parser, att, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
          error = MSGCAT_RUNTIME_RESOURCES_EXHAUSTED;
          goto exit_on_error;
        }
          *not_nulls = parser_append_node (pointer, *not_nulls);
        }
    }           /* for ( ; attr; ...) */
    }               /* for ( ; assignment; ...) */

  return NO_ERROR;

exit_on_error:
  if (*not_nulls)
    {
      parser_free_tree (parser, *not_nulls);
      *not_nulls = NULL;
    }
  return error;
}

/*
 * update_check_for_meta_attr () -
 *   return: true if update assignment clause has class or shared attribute
 *   parser(in): Parser context
 *   assignment(in): Parse tree of an assignment clause
 *
 * Note:
 */
static bool
update_check_having_meta_attr (PARSER_CONTEXT * parser, PT_NODE * assignment)
{
  PT_NODE *lhs, *att;

  for (; assignment; assignment = assignment->next)
    {
      lhs = assignment->info.expr.arg1;
      if (lhs->node_type == PT_NAME)
    {
      att = lhs;
    }
      else if (PT_IS_N_COLUMN_UPDATE_EXPR (lhs))
    {
      att = lhs->info.expr.arg1;
    }
      else
    {
      att = NULL;
    }

      for (; att; att = att->next)
    {
      if (att->node_type == PT_NAME && att->info.name.meta_class != PT_NORMAL)
        {
          return true;
        }
    }
    }

  return false;
}

/*
 * update_real_class() -
 *   return: Error code if update fails
 *   parser(in): Parser context
 *   statement(in): Parse tree of a update statement
 *   savepoint_started(in): true, if savepoint started
 *
 * Note: If the statement is of type "update class foo ...", this
 *   routine updates class attributes of foo.  If the statement is of
 *   type "update foo ...", this routine updates objects or rows in foo.
 *   It is assumed that class attributes and regular attributes
 *   are not mixed in the same update statement.
 */
static int
update_real_class (PARSER_CONTEXT * parser, PT_NODE * statement, bool savepoint_started)
{
  int error = NO_ERROR;
  PT_NODE *non_null_attrs = NULL, *spec = statement->info.update.spec;
  DB_OBJECT *class_obj = NULL;
  int server_allowed = 0;
  int has_uniques = 0;
  PT_NODE **links = NULL;

  /* update a "real" class in this database */

  while (spec)
    {
      /* Safety check: make sure that we have access to the class. We're only setting a weak lock here which guarantees
       * that the schema for the classes which are updated in this query is not changed. The correct lock for this
       * operation will be set server side when the SELECT part of the operation is being performed. */
      if (spec->info.spec.remote_server_name)
    {
      /* always server update for dblink query */
      server_allowed = 1;
      break;
    }

      if ((spec->info.spec.flag & PT_SPEC_FLAG_UPDATE) && spec->info.spec.flat_entity_list)
    {
      class_obj = spec->info.spec.flat_entity_list->info.name.db_object;
      if (!locator_fetch_class (class_obj, DB_FETCH_READ))
        {
          goto exit_on_error;
        }
    }
      spec = spec->next;
    }

  if (!server_allowed
      && is_server_update_allowed (parser, &non_null_attrs, &has_uniques, &server_allowed, statement) != NO_ERROR)
    {
      goto exit_on_error;
    }

  if (server_allowed)
    {
      /* do update on server */
      error = update_at_server (parser, spec, statement, &non_null_attrs, has_uniques);
    }
  else
    {
      PT_NODE *lhs = NULL;
      PT_NODE *select_names = NULL;
      PT_NODE *select_values = NULL;
      PT_NODE *const_names = NULL;
      PT_NODE *const_values = NULL;
      QFILE_LIST_ID *oid_list = NULL;
      int no_vals = 0;
      int no_consts = 0;
      int wait_msecs = -2;
      int old_wait_msecs = -2;
      float hint_waitsecs;

      /* do update on client */
      lhs = statement->info.update.assignment->info.expr.arg1;
      if (PT_IS_N_COLUMN_UPDATE_EXPR (lhs))
    {
      lhs = lhs->info.expr.arg1;
    }
      if (lhs->info.name.meta_class != PT_META_ATTR)
    {
      QUERY_ID query_id_self;
      PT_NODE *hint_arg;

      query_id_self = parser->query_id;
      parser->query_id = NULL_QUERY_ID;

      hint_arg = statement->info.update.waitsecs_hint;
      if (statement->info.update.hint & PT_HINT_LK_TIMEOUT && PT_IS_HINT_NODE (hint_arg))
        {
          hint_waitsecs = (float) atof (hint_arg->info.name.original);
          if (hint_waitsecs > 0)
        {
          wait_msecs = (int) (hint_waitsecs * 1000);
        }
          else
        {
          wait_msecs = (int) hint_waitsecs;
        }
          if (wait_msecs >= -1)
        {
          old_wait_msecs = TM_TRAN_WAIT_MSECS ();
          (void) tran_reset_wait_times (wait_msecs);
        }
        }
      if (error == NO_ERROR)
        {
          error =
        pt_get_assignment_lists (parser, &select_names, &select_values, &const_names, &const_values, &no_vals,
                     &no_consts, statement->info.update.assignment, &links);
          if (error != NO_ERROR)
        {
          goto exit_on_error;
        }
          /* get the oid's and new values */
          oid_list =
        get_select_list_to_update (parser, statement->info.update.spec, select_names, select_values,
                       statement->info.update.with, statement->info.update.search_cond,
                       statement->info.update.order_by, statement->info.update.orderby_for,
                       statement->info.update.using_index, statement->info.update.class_specs,
                       statement);

          /* restore tree structure */
          pt_restore_assignment_links (statement->info.update.assignment, links, -1);
        }
      if (old_wait_msecs >= -1)
        {
          (void) tran_reset_wait_times (old_wait_msecs);
        }

      if (!oid_list)
        {
          parser->query_id = query_id_self;
          /* an error should be set already, don't lose it */
          error = ER_GENERIC_ERROR;
          goto exit_on_error;
        }

      /* update each oid */
      error = update_objs_for_list_file (parser, oid_list, statement, savepoint_started);

      cursor_free_self_list_id (oid_list);
      pt_end_query (parser, query_id_self);
    }
      else
    {
      /* we are updating class attributes */
      error = update_class_attributes (parser, statement);
    }
    }

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

  return error;

exit_on_error:

  if (error == NO_ERROR)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
    }
  if (non_null_attrs != NULL)
    {
      parser_free_tree (parser, non_null_attrs);
      non_null_attrs = NULL;
    }
  return error;
}

/*
 * is_server_update_allowed() - Checks to see if a server-side update is
 *                              allowed
 *   return: NO_ERROR or error code on failure
 *   parser(in): Parser context
 *   non_null_attrs(in/out): Parse tree for attributes with the NOT NULL
 *                           constraint
 *   has_uniques(in/out): whether unique indexes are affected by the update
 *   server_allowed(in/out): whether the update can be executed on the server
 *   statement(in): Parse tree of an update statement
 */
static int
is_server_update_allowed (PARSER_CONTEXT * parser, PT_NODE ** non_null_attrs, int *has_uniques,
              int *const server_allowed, const PT_NODE * statement)
{
  int error = NO_ERROR;
  int is_partition = 0;
  int trigger_involved = 0, ti = 0, is_virt = 0;
  PT_NODE *spec = statement->info.update.spec;
  DB_OBJECT *class_obj = NULL;
  int save_au;

  *has_uniques = 0;
  *server_allowed = 0;

  AU_DISABLE (save_au);

  /* check if at least one spec that will be updated is virtual or has triggers */
  while (spec && !trigger_involved && !is_virt)
    {
      if (!(spec->info.spec.flag & PT_SPEC_FLAG_UPDATE))
    {
      spec = spec->next;
      continue;
    }
      class_obj = spec->info.spec.flat_entity_list->info.name.db_object;
      error = sm_partitioned_class_type (class_obj, &is_partition, NULL, NULL);
      if (error != NO_ERROR)
    {
      goto error_exit;
    }

      /* If the class is partitioned and has any type of trigger, the update must be executed on the client. */
      error = sm_class_has_triggers (class_obj, &ti, (is_partition ? TR_EVENT_ALL : TR_EVENT_UPDATE));
      if (error != NO_ERROR)
    {
      goto error_exit;
    }

      if (ti)
    {
      trigger_involved = ti;
    }

      is_virt = (spec->info.spec.flat_entity_list->info.name.virt_object != NULL);

      spec = spec->next;
    }

  error = update_check_for_constraints (parser, has_uniques, non_null_attrs, statement);
  if (error < NO_ERROR)
    {
      goto error_exit;
    }

  /* Check to see if the update can be done on the server */
  *server_allowed = ((!trigger_involved && !is_virt)
             && !update_check_having_meta_attr (parser, statement->info.update.assignment));

  AU_ENABLE (save_au);
  return error;

error_exit:
  if (non_null_attrs != NULL && *non_null_attrs != NULL)
    {
      parser_free_tree (parser, *non_null_attrs);
      *non_null_attrs = NULL;
    }
  AU_ENABLE (save_au);
  return error;
}

/*
 * do_update() - Updates objects or rows
 *   return: Error code if update fails
 *   parser(in): Parser context
 *   statement(in): Parse tree of a update statement
 *
 * Note:
 */
int
do_update (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  int result = NO_ERROR;
  const char *savepoint_name = NULL;
  bool savepoint_started = false;

  CHECK_MODIFICATION_ERROR ();

  /* DON'T REMOVE this, correct authorization validation of views depends on this. DON'T return from the body of this
   * function. Break out of the loop if necessary. */
  AU_DISABLE (parser->au_save);

  /* savepoint for statement atomicity */
  if ((statement != NULL && statement->next != NULL)
      || (TM_TRAN_ISOLATION () >= TRAN_REP_READ
      && (statement->info.update.object != NULL || !statement->info.update.server_update)))
    {
      savepoint_name = mq_generate_name (parser, "UmusP", &update_savepoint_number);
      if (savepoint_name == NULL)
    {
      error = ER_GENERIC_ERROR;
      goto end;
    }
      error = tran_system_savepoint (savepoint_name);
      if (error != NO_ERROR)
    {
      goto end;
    }

      savepoint_started = true;
    }

  while (statement && (error >= 0))
    {
      if (statement->node_type != PT_UPDATE || statement->info.update.assignment == NULL)
    {
      /* bullet proofing, should not get here */
      PT_INTERNAL_ERROR (parser, "update");
      error = ER_GENERIC_ERROR;
      break;
    }

      if (pt_false_where (parser, statement))
    {
      /* nothing to update, where part is false */
    }
      else if (statement->info.update.object != NULL)
    {
      /* this is a update object if it has an object */
      error = update_object_by_oid (parser, statement, UPDATE_OBJECT);
    }
      else
    {
      /* the following is the "normal" sql type execution */
      error = update_real_class (parser, statement, savepoint_started);
    }

      if (error < NO_ERROR && er_errid () != NO_ERROR)
    {
      pt_record_error (parser, parser->statement_number, statement->line_number, statement->column_number,
               er_msg (), NULL);
    }

      result += error;
      statement = statement->next;
    }

  /* if error and a savepoint was created, rollback to savepoint. No need to rollback if the TM aborted the
   * transaction. */
  if (error < NO_ERROR && savepoint_name && error != ER_LK_UNILATERALLY_ABORTED)
    {
      db_abort_to_savepoint (savepoint_name);
    }

end:
  if (error < 0)
    {
      result = error;
    }

  /* DON'T REMOVE this, correct authorization validation of views depends on this. */
  AU_ENABLE (parser->au_save);

  return result;
}

/*
 * do_prepare_update() - Prepare the UPDATE statement
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in/out): Parse tree of a update statement
 *
 * Note:
 */
int
do_prepare_update (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int err;
  PT_NODE *flat, *not_nulls, *lhs, *spec = NULL;
  DB_OBJECT *class_obj;
  int has_trigger, has_unique, has_any_update_trigger, au_save, has_virt = 0;
  bool server_update;

  if (parser == NULL || statement == NULL)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  for (err = NO_ERROR; statement && (err >= NO_ERROR); statement = statement->next)
    {
      COMPILE_CONTEXT *contextp;
      XASL_STREAM stream;

      contextp = &parser->context;

      init_xasl_stream (&stream);

      contextp->sql_user_text = statement->sql_user_text;
      contextp->sql_user_text_len = statement->sql_user_text_len;

      /* there can be no results, this is a compile time false where clause */
      if (pt_false_where (parser, statement))
    {
      /* tell to the execute routine that there's no XASL to execute */
      statement->xasl_id = NULL;
      err = NO_ERROR;
      continue;     /* continue to next UPDATE statement */
    }

      /*
       * Update object case:
       *   this is a update object if it has an object
       */
      if (statement->info.update.object)
    {
      statement->etc = NULL;
      err = NO_ERROR;
      continue;     /* continue to next UPDATE statement */
    }

      /* if already prepared */
      if (statement->xasl_id)
    {
      continue;     /* continue to next UPDATE statement */
    }

      AU_SAVE_AND_DISABLE (au_save);    /* because sm_class_has_trigger() calls au_fetch_class() */

      /* check if at least one spec to be updated has triggers. If none has triggers then see if at least one is
       * virtual */
      spec = statement->info.update.spec;
      has_trigger = 0;
      has_any_update_trigger = 0;
      while (spec && !has_trigger && err == NO_ERROR)
    {
      if (spec->info.spec.remote_server_name)
        {
          spec = spec->next;
          continue;
        }
      if (spec->info.spec.flag & PT_SPEC_FLAG_UPDATE)
        {
          flat = spec->info.spec.flat_entity_list;
          class_obj = (flat) ? flat->info.name.db_object : NULL;
          assert (class_obj);   /* safeguard */
          /* the presence of a proxy trigger should force the update to be performed through the workspace */
          err = sm_class_has_triggers (class_obj, &has_trigger, TR_EVENT_UPDATE);

          if (err == NO_ERROR)
        {
          if (has_trigger)
            {
              has_any_update_trigger = has_trigger;
            }
          else if (!has_any_update_trigger)
            {
              /* Check for statement delete triggerrs. */
              err = sm_class_has_triggers (class_obj, &has_any_update_trigger, TR_EVENT_STATEMENT_UPDATE);
            }
        }

          if (!has_virt)
        {
          has_virt = (flat->info.name.virt_object != NULL);
        }
        }

      spec = spec->next;
    }
      AU_RESTORE (au_save);

      /* err = has_proxy_trigger(flat, &has_trigger); */
      if (err != NO_ERROR)
    {
      PT_INTERNAL_ERROR (parser, "update");
      break;        /* stop while loop if error */
    }
      /* sm_class_has_triggers() checked if the class has active triggers */
      statement->info.update.has_trigger = (bool) has_trigger;

      if (statement->info.update.spec->info.spec.remote_server_name)
    {
      not_nulls = NULL;
    }
      else
    {
      /* check if the target class has UNIQUE constraint and get attributes that has NOT NULL constraint */
      err = update_check_for_constraints (parser, &has_unique, &not_nulls, statement);
      if (err < NO_ERROR)
        {
          PT_INTERNAL_ERROR (parser, "update");
          break;        /* stop while loop if error */
        }
    }

      statement->info.update.has_unique = (bool) has_unique;

      /* determine whether it can be server-side or OID list update */
      server_update = (!has_trigger && !has_virt
               && !update_check_having_meta_attr (parser, statement->info.update.assignment));

      lhs = statement->info.update.assignment->info.expr.arg1;
      if (PT_IS_N_COLUMN_UPDATE_EXPR (lhs))
    {
      lhs = lhs->info.expr.arg1;
    }
      statement->info.update.server_update = server_update;
      if (server_update && !has_any_update_trigger && !(statement->info.update.hint & PT_HINT_USE_SBR))
    {
      statement->info.update.execute_with_commit_allowed = 1;
    }

      /* if we are updating class attributes, not need to prepare */
      if (lhs->info.name.meta_class == PT_META_ATTR)
    {
      statement->info.update.do_class_attrs = true;
      continue;     /* continue to next UPDATE statement */
    }

      if (server_update)
    {
      /*
       * Server-side update case: (by requesting server to execute XASL)
       *  build UPDATE_PROC XASL
       */

      /* make query string */
      parser->flag.dont_prt_long_string = 1;
      parser->flag.long_string_skipped = 0;
      parser->flag.print_type_ambiguity = 0;
      PT_NODE_PRINT_TO_ALIAS (parser, statement, CUSTOM_PRINT_4_SHA_COMPUTE | PT_PRINT_LOWER);
      contextp->sql_hash_text = (char *) statement->alias_print;
      err =
        SHA1Compute ((unsigned char *) contextp->sql_hash_text, (unsigned) strlen (contextp->sql_hash_text),
             &contextp->sha1);
      if (err != NO_ERROR)
        {
          ASSERT_ERROR ();
          return err;
        }
      parser->flag.dont_prt_long_string = 0;
      if (parser->flag.long_string_skipped || parser->flag.print_type_ambiguity)
        {
          statement->flag.cannot_prepare = 1;
          return NO_ERROR;
        }
    }

      stream.xasl_id = NULL;
      if (server_update)
    {
      /*
       * Server-side update case: (by requesting server to execute XASL)
       *  build UPDATE_PROC XASL
       */

      /* look up server's XASL cache for this query string and get XASL file id (XASL_ID) returned if found */
      contextp->recompile_xasl = statement->flag.recompile;
      if (statement->flag.recompile == 0)
        {
          err = prepare_query (contextp, &stream);

          if (err != NO_ERROR)
        {
          ASSERT_ERROR_AND_SET (err);
        }
          else if (contextp->recompile_xasl == true)
        {
          /* recompile requested by server */
          if (stream.xasl_id != NULL)
            {
              free_and_init (stream.xasl_id);
            }
        }
        }

      if (stream.xasl_id == NULL && err == NO_ERROR)
        {
          /* cache not found; make XASL from the parse tree including query optimization and plan generation */

          /* mark the beginning of another level of xasl packing */
          pt_enter_packing_buf ();

          /* this prevents authorization checking during generating XASL */
          AU_SAVE_AND_DISABLE (au_save);

          /* pt_to_update_xasl() will build XASL tree from parse tree */
          contextp->xasl = pt_to_update_xasl (parser, statement, &not_nulls);
          AU_RESTORE (au_save);

          if (contextp->xasl && (err >= NO_ERROR))
        {
          int i;
          UPDATE_PROC_NODE *update = &contextp->xasl->proc.update;

          /* convert the created XASL tree to the byte stream for transmission to the server */
          err = xts_map_xasl_to_stream (contextp->xasl, &stream);
          if (err != NO_ERROR)
            {
              PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
            }
          for (i = update->num_assigns - 1; i >= 0; i--)
            {
              if (update->assigns[i].constant)
            {
              pr_clear_value (update->assigns[i].constant);
            }
            }
        }
          else
        {
          assert (er_errid () != NO_ERROR);
          err = er_errid ();
          pt_record_error (parser, parser->statement_number, statement->line_number, statement->column_number,
                   er_msg (), NULL);
        }

          /* request the server to prepare the query; give XASL stream generated from the parse tree and get XASL
           * file id returned */
          if (stream.buffer && (err >= NO_ERROR))
        {
          err = prepare_query (contextp, &stream);

          if (err != NO_ERROR)
            {
              assert (er_errid () != NO_ERROR);
              err = er_errid ();
            }
        }

          /* mark the end of another level of xasl packing */
          pt_exit_packing_buf ();

          /* As a result of query preparation of the server, the XASL cache for this query will be created or
           * updated. */

          /* free 'stream' that is allocated inside of xts_map_xasl_to_stream() */
          if (stream.buffer)
        {
          free_and_init (stream.buffer);
        }
          statement->flag.use_plan_cache = 0;
        }
      else
        {           /* if (!xasl_id) */
          spec = statement->info.update.spec;
          while (spec && err == NO_ERROR)
        {
          flat = spec->info.spec.flat_entity_list;
          while (flat)
            {
              if (locator_flush_class (flat->info.name.db_object) != NO_ERROR)
            {
              stream.xasl_id = NULL;
              assert (er_errid () != NO_ERROR);
              err = er_errid ();
              break;
            }
              flat = flat->next;
            }
          spec = spec->next;
        }
          if (err == NO_ERROR)
        {
          statement->flag.use_plan_cache = 1;
        }
          else
        {
          statement->flag.use_plan_cache = 0;
        }
        }
    }
      else
    {           /* if (server_update) */
      /*
       * OID list update case: (by selecting OIDs to update)
       *  make SELECT statement for this UPDATE statement
       */
      PT_NODE *select_statement = NULL;
      PT_NODE *select_names = NULL, *select_values = NULL;
      PT_NODE *const_names = NULL, *const_values = NULL;
      PT_NODE **links = NULL;
      int no_vals = 0, no_consts = 0;

      PT_NODE *assigns = statement->info.update.assignment;
      PT_NODE *from = statement->info.update.spec;

      err = pt_append_omitted_on_update_expr_assignments (parser, assigns, from);
      if (err != NO_ERROR)
        {
          PT_INTERNAL_ERROR (parser, "update");
          break;        /* stop while loop if error */
        }

      err = pt_get_assignment_lists (parser, &select_names, &select_values, &const_names, &const_values, &no_vals,
                     &no_consts, statement->info.update.assignment, &links);
      if (err != NO_ERROR)
        {
          PT_INTERNAL_ERROR (parser, "update");
          break;        /* stop while loop if error */
        }

      /* make sure that lhs->info.name.meta_class != PT_META_ATTR */
      select_statement =
        pt_to_upd_del_query (parser, select_names, select_values, statement->info.update.spec,
                 statement->info.update.with, statement->info.update.class_specs,
                 statement->info.update.search_cond, statement->info.update.using_index,
                 statement->info.update.order_by, statement->info.update.orderby_for, 0, S_UPDATE);

      /* restore tree structure; pt_get_assignment_lists() */
      pt_restore_assignment_links (statement->info.update.assignment, links, -1);

      /* translate views or virtual classes into base classes; If we are updating a proxy, the SELECT is not yet
       * fully translated. If we are updating anything else, this is a no-op. */

      /* this prevents authorization checking during view transformation */
      AU_SAVE_AND_DISABLE (au_save);

      if (select_statement != NULL)
        {
          err = pt_copy_upddel_hints_to_select (parser, statement, select_statement);
          if (err != NO_ERROR)
        {
          parser_free_tree (parser, select_statement);
          break;
        }
        }
      select_statement = mq_translate (parser, select_statement);
      AU_RESTORE (au_save);
      if (select_statement)
        {
          /* get XASL_ID by calling do_prepare_select() */
          err = do_prepare_select (parser, select_statement);
          stream.xasl_id = select_statement->xasl_id;
          select_statement->xasl_id = NULL;
          parser_free_tree (parser, select_statement);
        }
      else
        {
          PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
          err = er_errid ();
        }

    }           /* else (server_update) */

      /* save the XASL_ID that is allocated and returned by prepare_query() into 'statement->xasl_id' to be used by
       * do_execute_update() */
      statement->xasl_id = stream.xasl_id;

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

  return err;
}

/*
 * do_execute_update() - Execute the prepared UPDATE statement
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in): Parse tree of a update statement
 *
 * Note:
 */
int
do_execute_update (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  INT64 err, result = 0;
  PT_NODE *flat, *spec = NULL;
  const char *savepoint_name = NULL;
  DB_OBJECT *class_obj;
  QFILE_LIST_ID *list_id;
  int au_save;
  int wait_msecs = -2, old_wait_msecs = -2;
  float hint_waitsecs;
  PT_NODE *hint_arg;
  QUERY_ID query_id_self = parser->query_id;
  bool savepoint_started = false;

  assert (parser->query_id == NULL_QUERY_ID);

  CHECK_MODIFICATION_ERROR ();

  /* savepoint for statement atomicity */
  if ((statement != NULL && statement->next != NULL)
      || (TM_TRAN_ISOLATION () >= TRAN_REP_READ
      && (statement->info.update.object != NULL || !statement->info.update.server_update)))
    {
      savepoint_name = mq_generate_name (parser, "UmusP", &update_savepoint_number);
      if (savepoint_name == NULL)
    {
      return ER_GENERIC_ERROR;
    }
      err = tran_system_savepoint (savepoint_name);
      if (err != NO_ERROR)
    {
      return err;
    }
      savepoint_started = true;
    }

  for (err = NO_ERROR, result = 0; statement && (err >= NO_ERROR); statement = statement->next)
    {
      /*
       * Update object case:
       *   update object by OID
       */
      if (statement->info.update.object)
    {
      err = update_object_by_oid (parser, statement, UPDATE_OBJECT);
      continue;     /* continue to next UPDATE statement */
    }

      /* check if it is not necessary to execute this statement, e.g. false where or not prepared correctly */
      if (!statement->info.update.do_class_attrs && !statement->xasl_id)
    {
      statement->etc = NULL;
      err = NO_ERROR;
      continue;     /* continue to next UPDATE statement */
    }

      /*
       * Server-side update or OID list update case:
       *  execute the prepared(stored) XASL (UPDATE_PROC or SELECT statement)
       */
      if (statement->info.update.server_update || !statement->info.update.do_class_attrs)
    {
      /* Request that the server executes the stored XASL, which is the execution plan of the prepared query, with
       * the host variables given by users as parameter values for the query. As a result, query id and result file
       * id (QFILE_LIST_ID) will be returned. do_prepare_update() has saved the XASL file id (XASL_ID) in
       * 'statement->xasl_id' */

      int query_flag = DEFAULT_EXEC_MODE;

      query_flag |= NOT_FROM_RESULT_CACHE;
      query_flag |= RESULT_CACHE_INHIBITED;

      if (parser->flag.is_xasl_pinned_reference)
        {
          query_flag |= XASL_CACHE_PINNED_REFERENCE;
        }

      if (statement->flag.use_auto_commit)
        {
          query_flag |= EXECUTE_QUERY_WITH_COMMIT;
        }

      if (parser->flag.is_auto_commit)
        {
          query_flag |= TRAN_AUTO_COMMIT;
        }

      if (prm_get_bool_value (PRM_ID_QUERY_TRACE) == true && parser->query_trace == true)
        {
          do_set_trace_to_query_flag (&query_flag);
          do_send_plan_trace_to_session (parser);
        }

      // When a transaction is under auto-commit mode, flush all dirty objects to server.
      // Otherwise, flush associated objects.
      if (statement->flag.use_auto_commit)
        {
          err = tran_flush_to_commit ();
        }
      else
        {
          /* flush necessary objects before execute */
          for (spec = statement->info.update.spec; spec != NULL && err == NO_ERROR; spec = spec->next)
        {
          if (spec->info.spec.flag & PT_SPEC_FLAG_UPDATE)
            {
              err = sm_flush_objects (spec->info.spec.flat_entity_list->info.name.db_object);
            }
        }
        }

      if (err != NO_ERROR)
        {
          // flush error
          break;
        }

      AU_SAVE_AND_ENABLE (au_save); /* this insures authorization checking for method */

      assert (parser->query_id == NULL_QUERY_ID);
      list_id = NULL;

      /* check host variables are filled : values from orderby_for may not have been auto-parameterized as host
       * variables; if that is the case, do it now */
      if (statement->info.update.orderby_for != NULL && parser->auto_param_count == 0)
        {
          assert ((parser->host_variables == NULL && parser->host_var_count == 0)
              || (parser->host_variables != NULL && parser->host_var_count > 0));

          qo_auto_parameterize (parser, statement->info.update.orderby_for);
        }

      err = execute_query (statement->xasl_id, &parser->query_id, parser->host_var_count + parser->auto_param_count,
                   parser->host_variables, &list_id, query_flag, NULL, NULL);

      AU_RESTORE (au_save);
      if (err != NO_ERROR)
        {
          break;        /* stop while loop if error */
        }
    }

      if (!statement->info.update.server_update)
    {
      hint_arg = statement->info.update.waitsecs_hint;
      if (statement->info.update.hint & PT_HINT_LK_TIMEOUT && PT_IS_HINT_NODE (hint_arg))
        {
          hint_waitsecs = (float) atof (hint_arg->info.name.original);
          if (hint_waitsecs > 0)
        {
          wait_msecs = (int) (hint_waitsecs * 1000);
        }
          else
        {
          wait_msecs = (int) hint_waitsecs;
        }
          if (wait_msecs >= -1)
        {
          old_wait_msecs = TM_TRAN_WAIT_MSECS ();
          (void) tran_reset_wait_times (wait_msecs);
        }
        }
      AU_SAVE_AND_DISABLE (au_save);    /* this prevents authorization checking during execution */
      if (statement->info.update.do_class_attrs)
        {
          /* in case of update class attributes, */
          err = update_class_attributes (parser, statement);
        }
      else
        {
          /* in the case of OID list update, now update the selected OIDs */
          err = update_objs_for_list_file (parser, list_id, statement, savepoint_started);
        }

      AU_RESTORE (au_save);
      if (old_wait_msecs >= -1)
        {
          (void) tran_reset_wait_times (old_wait_msecs);
        }
    }

      if (statement->info.update.server_update || !statement->info.update.do_class_attrs)
    {
      /* free returned QFILE_LIST_ID */
      if (list_id)
        {
          if (list_id->tuple_cnt > 0 && statement->info.update.server_update)
        {
          spec = statement->info.update.spec;
          while (spec && err == NO_ERROR)
            {
              if (spec->info.spec.flag & PT_SPEC_FLAG_UPDATE)
            {
              flat = spec->info.spec.flat_entity_list;
              class_obj = (flat) ? flat->info.name.db_object : NULL;
              if (statement->flag.use_auto_commit && tran_was_latest_query_committed ())
                {
                  /* Nothing to flush. Avoids flush, since may fetch the class. */
                  err = sm_decache_instances_after_query_executed_with_commit (class_obj);
                }
              else
                {
                  err = sm_flush_and_decache_objects (class_obj, true);
                }
            }

              spec = spec->next;
            }
        }
          if (err >= NO_ERROR && statement->info.update.server_update)
        {
          err = list_id->tuple_cnt; /* as a result */
        }
          cursor_free_self_list_id (list_id);
        }
      /* end the query; reset query_id and call qmgr_end_query() */
      pt_end_query (parser, query_id_self);
    }

      /* accumulate intermediate results */
      if (err >= NO_ERROR)
    {
      result += err;
    }

      spec = statement->info.update.spec;
      while (spec && err == NO_ERROR)
    {
      if (spec->info.spec.flag & PT_SPEC_FLAG_UPDATE)
        {
          flat = spec->info.spec.flat_entity_list;
          class_obj = (flat) ? flat->info.name.db_object : NULL;

          if (class_obj && (statement->flag.use_auto_commit == false || !tran_was_latest_query_committed ()))
        {
          err = db_is_vclass (class_obj);
          if (err > 0)
            {
              err = sm_flush_objects (class_obj);
            }
        }

        }

      spec = spec->next;
    }

      if ((err < NO_ERROR) && er_errid () != NO_ERROR)
    {
      pt_record_error (parser, parser->statement_number, statement->line_number, statement->column_number,
               er_msg (), NULL);
    }
    }

  /* If error and a savepoint was created, rollback to savepoint. No need to rollback if the TM aborted the
   * transaction. */
  if (err < NO_ERROR && savepoint_name && err != ER_LK_UNILATERALLY_ABORTED)
    {
      db_abort_to_savepoint (savepoint_name);
    }

  return (err < NO_ERROR) ? err : result;
}


/*
 * Function Group:
 * DO functions for delete statements
 *
 */

/* used to generate unique savepoint names */
static int delete_savepoint_number = 0;

static int select_delete_list (PARSER_CONTEXT * parser, QFILE_LIST_ID ** result_p, PT_NODE * delete_stmt);
#if defined(ENABLE_UNUSED_FUNCTION)
static int delete_object_by_oid (const PARSER_CONTEXT * parser, const PT_NODE * statement);
#endif /* ENABLE_UNUSED_FUNCTION */
static int delete_list_by_oids (PARSER_CONTEXT * parser, PT_NODE * statement, QFILE_LIST_ID * list_id,
                bool savepoint_started);
static int build_xasl_for_server_delete (PARSER_CONTEXT * parser, PT_NODE * statement);
static int delete_real_class (PARSER_CONTEXT * parser, PT_NODE * statement);

/*
 * select_delete_list() -
 *   return: Error code
 *   parser(in/out): Parser context
 *   result(out): QFILE_LIST_ID for query result
 *   delete_stmt(in): delete statement
 *
 * Note : The list_id is allocated during query execution
 */
static int
select_delete_list (PARSER_CONTEXT * parser, QFILE_LIST_ID ** result_p, PT_NODE * delete_stmt)
{
  PT_NODE *statement = NULL;
  QFILE_LIST_ID *result = NULL;
  int ret = NO_ERROR;

  assert (parser->query_id == NULL_QUERY_ID);
  assert (delete_stmt->info.delete_.with == NULL);

  statement = pt_to_upd_del_query (parser, NULL, NULL, delete_stmt->info.delete_.spec, delete_stmt->info.delete_.with,
                   delete_stmt->info.delete_.class_specs, delete_stmt->info.delete_.search_cond,
                   delete_stmt->info.delete_.using_index, NULL, NULL,
                   0 /* not server update */ , S_DELETE);
  if (statement != NULL)
    {
      ret = pt_copy_upddel_hints_to_select (parser, delete_stmt, statement);
      if (ret != NO_ERROR)
    {
      parser_free_tree (parser, statement);
      return ret;
    }
      /* If we are updating a proxy, the select is not yet fully translated. if we are updating anything else, this is
       * a no-op. */
      statement = mq_translate (parser, statement);

      if (statement)
    {
      /* This enables authorization checking during methods in queries */
      AU_ENABLE (parser->au_save);

      assert (parser->query_id == NULL_QUERY_ID);
      if (do_select (parser, statement) < NO_ERROR)
        {
          /* query failed, an error has already been set */
          statement = NULL;
        }

      AU_DISABLE (parser->au_save);
    }
    }

  if (statement)
    {
      result = (QFILE_LIST_ID *) statement->etc;
      parser_free_tree (parser, statement);
    }

  *result_p = result;

  if (ret == NO_ERROR)
    {
      if (er_errid () != NO_ERROR)
    {
      ret = er_errid ();
    }
    }

  return ret;
}

/*
 * delete_object_tuple() - Deletes object attributes with db_values
 *   return: Error code if db_put fails
 *   object(in): object to delete
 */
static int
delete_object_tuple (DB_OBJECT * object)
{
  int error = NO_ERROR;
  DB_OBJECT *class_obj;
  DB_ATTRIBUTE *attr;
  if (object == NULL)
    {
      error = ER_OBJ_INVALID_ARGUMENTS;
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 0);
    }

  /* authorizations checked in compiler--turn off but remember in parser so we can re-enable in case we run out of
   * memory and longjmp to the cleanup routine. */

  /* delete blob or clob data files if exist */
  class_obj = db_get_class (object);
  attr = db_get_attributes (class_obj);
  while (attr)
    {
      if (attr->type->id == DB_TYPE_BLOB || attr->type->id == DB_TYPE_CLOB)
    {
      DB_VALUE dbvalue;
      error = db_get (object, attr->header.name, &dbvalue);
      if (error == NO_ERROR && !DB_IS_NULL (&dbvalue))
        {
          DB_ELO *elo;

          assert (db_value_type (&dbvalue) == DB_TYPE_BLOB || db_value_type (&dbvalue) == DB_TYPE_CLOB);
          elo = db_get_elo (&dbvalue);
          if (elo)
        {
          error = db_elo_delete (elo);
        }
          db_value_clear (&dbvalue);
          error = (error >= 0 ? NO_ERROR : error);
        }
      if (error != NO_ERROR)
        {
          er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, error, 0);
          return error;
        }
    }
      attr = db_attribute_next (attr);
    }
  /* TODO: to delete blob or clob at db api call */
  error = db_drop (object);

  return error;
}

#if defined(ENABLE_UNUSED_FUNCTION)
/*
 * delete_object_by_oid() - Deletes db object by oid.
 *   return: Error code if delete fails
 *   parser(in): Parser context
 *   statement(in): Parse tree representing object to delete
 */
static int
delete_object_by_oid (const PARSER_CONTEXT * parser, const PT_NODE * statement)
{
  int error = NO_ERROR;

  error = ER_GENERIC_ERROR; /* unimplemented */

  return error;
}
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * delete_list_by_oids() - Deletes every oid in a list file
 *   return: Error code if delete fails
 *   parser(in): Parser context
 *   statement(in): Delete statement
 *   list_id(in): A list file of oid's
 *   savepoint_started(in): true, if savepoint already started
 */
static int
delete_list_by_oids (PARSER_CONTEXT * parser, PT_NODE * statement, QFILE_LIST_ID * list_id, bool savepoint_started)
{
  int error = NO_ERROR;
  int cursor_status;
  DB_VALUE *oids = NULL;
  CURSOR_ID cursor_id;
  int count = 0, attrs_cnt = 0, idx;    /* how many objects were deleted? */
  const char *savepoint_name = NULL;
  int *flush_to_server = NULL;
  DB_OBJECT *mop = NULL, *class_obj = NULL;
  bool has_savepoint = false, is_cursor_open = false;
  PT_NODE *spec;

  if (list_id == NULL)
    {
      return NO_ERROR;
    }

  assert (parser->query_id != NULL_QUERY_ID);
  assert (parser->query_id == list_id->query_id);

  spec = statement->info.delete_.spec;
  while (spec)
    {
      if (spec->info.spec.flag & PT_SPEC_FLAG_DELETE)
    {
      class_obj = spec->info.spec.flat_entity_list->info.name.db_object;
      /* place IX lock on the class. This should have been done already when the list_id was produced but this is
       * the last opportunity we have before actually deleting objects. */
      if (locator_fetch_class (class_obj, DB_FETCH_CLREAD_INSTWRITE) == NULL)
        {
          assert (er_errid () != NO_ERROR);
          return er_errid ();
        }
    }
      spec = spec->next;
    }

  /* if the list file contains more than 1 object we need to savepoint the statement to guarantee statement atomicity. */
  if (list_id->tuple_cnt >= 1 || TM_TRAN_ISOLATION () >= TRAN_REP_READ)
    {
      if (savepoint_started == false)
    {
      savepoint_name = mq_generate_name (parser, "UdsP", &delete_savepoint_number);

      error = tran_system_savepoint (savepoint_name);
      if (error != NO_ERROR)
        {
          goto cleanup;
        }
      has_savepoint = true;
    }
    }

  if (!cursor_open (&cursor_id, list_id, false, false))
    {
      error = ER_GENERIC_ERROR;
      goto cleanup;
    }
  is_cursor_open = true;
  attrs_cnt = list_id->type_list.type_cnt;

  oids = (DB_VALUE *) db_private_alloc (NULL, attrs_cnt * sizeof (DB_VALUE));
  if (oids == NULL)
    {
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto cleanup;
    }
  flush_to_server = (int *) db_private_alloc (NULL, attrs_cnt * sizeof (int));
  if (flush_to_server == NULL)
    {
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto cleanup;
    }
  for (idx = 0; idx < attrs_cnt; idx++)
    {
      flush_to_server[idx] = -1;
    }

  cursor_id.query_id = parser->query_id;
  cursor_status = cursor_next_tuple (&cursor_id);

  while ((error >= NO_ERROR) && cursor_status == DB_CURSOR_SUCCESS)
    {
      error = cursor_get_tuple_value_list (&cursor_id, attrs_cnt, oids);
      /* The select list contains instance OID - class OID pairs */
      for (idx = 0; idx < attrs_cnt && error >= NO_ERROR; idx++)
    {
      if (DB_IS_NULL (&oids[idx]))
        {
          continue;
        }

      mop = db_get_object (&oids[idx]);

      error = db_is_deleted (mop);
      if (error < 0)
        {
          return error;
        }
      if (error > 0)
        {
          /* if the object is an invalid object (was already deleted) then skip the delete, instance flush and
           * count steps */
          error = NO_ERROR;
          continue;
        }

      if (flush_to_server[idx] == -1)
        {
          /* the following code may be improved to not flush every time */
          if (has_unique_constraint (mop) || TM_TRAN_ISOLATION () >= TRAN_REP_READ)
        {
          flush_to_server[idx] = 1;
        }
          else
        {
          flush_to_server[idx] = 0;
        }
        }

      // In has_unique_constraint (mop) is just for checking whether flushing to server is required, an error should be ignored.
      if (er_errid () == ER_OBJ_NO_COMPONENTS)
        {
          er_clear ();
        }

      error = delete_object_tuple (mop);
      if (error == ER_HEAP_UNKNOWN_OBJECT && do_Trigger_involved)
        {
          er_clear ();
          error = NO_ERROR;
          continue;
        }

      if ((error >= NO_ERROR) && flush_to_server[idx])
        {
          error = (locator_flush_instance (mop) == NO_ERROR) ? 0 : (er_errid () != NO_ERROR ? er_errid () : -1);
        }

      if (error >= NO_ERROR)
        {
          count++;      /* another object has been deleted */
        }
    }

      if (error >= NO_ERROR)
    {
      cursor_status = cursor_next_tuple (&cursor_id);
    }
    }

  if ((error >= NO_ERROR) && cursor_status != DB_CURSOR_END)
    {
      error = ER_GENERIC_ERROR;
    }

cleanup:
  if (is_cursor_open)
    {
      cursor_close (&cursor_id);
    }

  /* if error and a savepoint was created, rollback to savepoint. No need to rollback if the TM aborted the transaction
   * itself. */
  if (has_savepoint && (error < NO_ERROR) && savepoint_name && error != ER_LK_UNILATERALLY_ABORTED)
    {
      (void) tran_abort_upto_system_savepoint (savepoint_name);
    }

  if (oids != NULL)
    {
      db_private_free (NULL, oids);
    }

  if (flush_to_server != NULL)
    {
      db_private_free (NULL, flush_to_server);
    }

  if (error >= NO_ERROR)
    {
      return count;
    }
  else
    {
      return error;
    }
}

/*
 * build_xasl_for_server_delete() - Build an xasl tree for a server delete
 *                                     and execute it.
 *   return: Error code if delete fails
 *   parser(in/out): Parser context
 *   statement(in): Parse tree of a delete statement.
 *
 * Note:
 *  The xasl tree has an DELETE_PROC node as the top node and
 *  a BUILDLIST_PROC as its aptr.  The BUILDLIST_PROC selects the
 *  instance OID.  The DELETE_PROC node scans the BUILDLIST_PROC results.
 *  The server executes the aptr and then for each instance selected,
 *  deletes it.  The result information is sent back to the
 *  client as a list file without any pages.  The list file tuple count
 *  is used as the return value from this routine.
 *
 *  The instances for the class are flushed from the client before the
 *  delete is executed.  If any instances are deleted, the instances are
 *  decached from the client after the delete is executed.
 */
static int
build_xasl_for_server_delete (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  XASL_NODE *xasl = NULL;
  DB_OBJECT *class_obj;
  INT64 count = 0;
  QUERY_ID query_id_self = parser->query_id;
  QFILE_LIST_ID *list_id = NULL;
  const PT_NODE *node;

  XASL_STREAM stream;

  assert (parser->query_id == NULL_QUERY_ID);

  init_xasl_stream (&stream);

  /* mark the beginning of another level of xasl packing */
  pt_enter_packing_buf ();

  xasl = pt_to_delete_xasl (parser, statement);

  if (xasl)
    {
      error = xts_map_xasl_to_stream (xasl, &stream);
      if (error != NO_ERROR)
    {
      PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
    }
    }
  else
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
    }

  if (error == NO_ERROR)
    {
      int au_save;
      QUERY_FLAG query_flag;

      query_flag = DEFAULT_EXEC_MODE;

      if (parser->flag.is_auto_commit)
    {
      query_flag |= TRAN_AUTO_COMMIT;
    }

      AU_SAVE_AND_ENABLE (au_save); /* this insures authorization checking for method */

      error =
    prepare_and_execute_query (stream.buffer, stream.buffer_size, &parser->query_id,
                   parser->host_var_count + parser->auto_param_count, parser->host_variables, &list_id,
                   query_flag);
      AU_RESTORE (au_save);
    }

  /* free 'stream' that is allocated inside of xts_map_xasl_to_stream() */
  if (stream.buffer)
    {
      free_and_init (stream.buffer);
    }

  if (list_id)
    {
      count = list_id->tuple_cnt;
      if (count > 0)
    {
      node = statement->info.delete_.spec;
      while (node && error == NO_ERROR)
        {
          if (node->info.spec.flat_entity_list && (node->info.spec.flag & PT_SPEC_FLAG_DELETE))
        {
          class_obj = node->info.spec.flat_entity_list->info.name.db_object;
          if (statement->flag.use_auto_commit && tran_was_latest_query_committed ())
            {
              /* Nothing to flush. Avoids flush, since may fetch the class. */
              error = sm_decache_instances_after_query_executed_with_commit (class_obj);
            }
          else
            {
              error = sm_flush_and_decache_objects (class_obj, true);
            }
        }

          node = node->next;
        }
    }
      cursor_free_self_list_id (list_id);
    }

  pt_end_query (parser, query_id_self);

  /* mark the end of another level of xasl packing */
  pt_exit_packing_buf ();

  if (error >= NO_ERROR)
    {
      return count;
    }
  else
    {
      return error;
    }
}

/*
 * has_unique_constraint() - Check if class has an unique constraint
 *   return: 1 if the class has an unique constraint, otherwise 0
 *   mop(in/out): Class object to be checked
 */
static int
has_unique_constraint (DB_OBJECT * mop)
{
  DB_CONSTRAINT *constraint_list, *c;
  SM_CONSTRAINT_TYPE ctype;

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

  constraint_list = db_get_constraints (mop);
  for (c = constraint_list; c; c = c->next)
    {
      ctype = c->type;
      if (SM_IS_CONSTRAINT_UNIQUE_FAMILY (ctype))
    {
      return 1;
    }
    }

  return 0;
}

/*
 * delete_real_class() - Deletes objects or rows
 *   return: Error code if delete fails
 *   parser(in/out): Parser context
 *   statement(in): Delete statement
 */
static int
delete_real_class (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  QFILE_LIST_ID *oid_list = NULL;
  int trigger_involved = 0;
  MOBJ class_;
  DB_OBJECT *class_obj;
  int wait_msecs = -2, old_wait_msecs = -2;
  float hint_waitsecs;
  PT_NODE *hint_arg = NULL, *spec = NULL;
  bool has_virt_object = false;

  /* delete a "real" class in this database */

  spec = statement->info.delete_.spec;
  while (spec)
    {
      if ((spec->info.spec.flag & PT_SPEC_FLAG_DELETE) && !spec->info.spec.remote_server_name)
    {
      class_obj = spec->info.spec.flat_entity_list->info.name.db_object;

      if (spec->info.spec.flat_entity_list->info.name.virt_object)
        {
          has_virt_object = true;
        }
      /* place weak lock here, we will upgrade it once the actual DELETE operation starts */
      class_ = locator_fetch_class (class_obj, DB_FETCH_READ);
      if (class_ == NULL)
        {
          assert (er_errid () != NO_ERROR);
          return er_errid ();
        }

      if (!trigger_involved)
        {
          error = sm_class_has_triggers (class_obj, &trigger_involved, TR_EVENT_DELETE);
          if (error != NO_ERROR)
        {
          return error;
        }
        }
    }

      spec = spec->next;
    }

  /* do delete on server if there is no trigger involved and the class is a real class */
  if (!trigger_involved && !has_virt_object)
    {
      error = build_xasl_for_server_delete (parser, statement);
    }
  else
    {
      QUERY_ID query_id_self;

      query_id_self = parser->query_id;
      parser->query_id = NULL_QUERY_ID;

      hint_arg = statement->info.delete_.waitsecs_hint;
      if (statement->info.delete_.hint & PT_HINT_LK_TIMEOUT && PT_IS_HINT_NODE (hint_arg))
    {
      hint_waitsecs = (float) atof (hint_arg->info.name.original);
      if (hint_waitsecs > 0)
        {
          wait_msecs = (int) (hint_waitsecs * 1000);
        }
      else
        {
          wait_msecs = (int) hint_waitsecs;
        }
      if (wait_msecs >= -1)
        {
          old_wait_msecs = TM_TRAN_WAIT_MSECS ();
          (void) tran_reset_wait_times (wait_msecs);
        }
    }
      if (error >= NO_ERROR)
    {
      /* get the oid's and new values */
      error = select_delete_list (parser, &oid_list, statement);
    }
      if (old_wait_msecs >= -1)
    {
      (void) tran_reset_wait_times (old_wait_msecs);
    }

      if (!oid_list)
    {
      parser->query_id = query_id_self;
      /* an error should be set already, don't lose it */
      return error;
    }

      /* delete each oid */
      error = delete_list_by_oids (parser, statement, oid_list, false);
      cursor_free_self_list_id (oid_list);
      pt_end_query (parser, query_id_self);
    }

  return error;
}

/*
 * do_delete() - Deletes objects or rows
 *   return: Error code if delete fails
 *   parser(in/out): Parser context
 *   statement(in): Delete statement
 *
 * Note: Returning the number of deleted object on success is a bug fix!
 *       uci_static expects do_execute_statement to return the number of
 *       affected objects for a successful DELETE, UPDATE, INSERT, SELECT.
 */
int
do_delete (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  int result = NO_ERROR;
  PT_NODE *spec;
  const char *savepoint_name = NULL;

  CHECK_MODIFICATION_ERROR ();

  /* DON'T REMOVE this, correct authorization validation of views depends on this.
   *
   * DON'T return from the body of this function. Break out of the loop if necessary. */

  AU_DISABLE (parser->au_save);

  /* savepoint for statement atomicity */
  if (statement != NULL && statement->next != NULL)
    {
      savepoint_name = mq_generate_name (parser, "UmdsP", &delete_savepoint_number);
      if (savepoint_name == NULL)
    {
      error = ER_GENERIC_ERROR;
      goto end;
    }
      error = tran_system_savepoint (savepoint_name);
      if (error != NO_ERROR)
    {
      goto end;
    }
    }

  while (statement && error >= 0)
    {
      if (statement->node_type != PT_DELETE)
    {
      /* bullet proofing, should not get here */
      PT_INTERNAL_ERROR (parser, "delete");
      error = ER_GENERIC_ERROR;
      break;
    }

      spec = statement->info.delete_.spec;

      if (pt_false_where (parser, statement))
    {
      /* nothing to delete, where part is false */
    }
#if defined(ENABLE_UNUSED_FUNCTION)
      else if (!spec)
    {
      /* this is an delete object if it has no spec */
      error = delete_object_by_oid (parser, statement);
    }
#endif /* ENABLE_UNUSED_FUNCTION */
      else
    {
      /* the following is the "normal" sql type execution */
      error = delete_real_class (parser, statement);
    }

      result += error;
      statement = statement->next;
    }

  /* if error and a savepoint was created, rollback to savepoint. No need to rollback if the TM aborted the
   * transaction. */

  if (error < NO_ERROR && savepoint_name && error != ER_LK_UNILATERALLY_ABORTED)
    {
      db_abort_to_savepoint (savepoint_name);
    }

end:
  if (error < 0)
    {
      result = error;
    }

  /* DON'T REMOVE this, correct authorization validation of views depends on this. */

  AU_ENABLE (parser->au_save);

  return result;
}

/*
 * do_prepare_delete() - Prepare the DELETE statement
 *   return: Error code
 *   parser(in/out): Parser context
 *   statement(in/out): Delete statement
 *   parent(in): Parent statement if using multi-delete list
 */
int
do_prepare_delete (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * parent)
{
  int err;
  PT_NODE *flat;
  DB_OBJECT *class_obj;
  int has_trigger, au_save, has_any_delete_trigger;
  bool server_delete, has_virt_obj;
  PT_NODE *node = NULL;
  PT_NODE *save_stmt = statement;

  if (parser == NULL)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  for (err = NO_ERROR; statement && (err >= NO_ERROR); statement = statement->next)
    {
      COMPILE_CONTEXT *contextp;
      XASL_STREAM stream;

      contextp = &parser->context;

      init_xasl_stream (&stream);

      contextp->sql_user_text = statement->sql_user_text;
      contextp->sql_user_text_len = statement->sql_user_text_len;

      /* there can be no results, this is a compile time false where clause */
      if (pt_false_where (parser, statement))
    {
      /* tell to the execute routine that there's no XASL to execute */
      statement->xasl_id = NULL;
      err = NO_ERROR;
      continue;     /* continue to next DELETE statement */
    }

      /* Delete object case: this is an delete object if it has no spec */
      if (!statement->info.delete_.spec)
    {
      statement->etc = NULL;
      err = NO_ERROR;
      continue;     /* continue to next DELETE statement */
    }

      /* if already prepared */
      if (statement->xasl_id)
    {
      continue;     /* continue to next DELETE statement */
    }

      err = pt_split_delete_stmt (parser, statement);
      if (err != NO_ERROR)
    {
      break;
    }

      /* the presence of a proxy trigger should force the delete to be performed through the workspace */
      AU_SAVE_AND_DISABLE (au_save);    /* because sm_class_has_trigger() calls au_fetch_class() */
      has_virt_obj = false;
      has_trigger = 0;
      has_any_delete_trigger = 0;
      node = (PT_NODE *) statement->info.delete_.spec;
      while (node && err == NO_ERROR && !has_trigger)
    {
      if (node->info.spec.remote_server_name)
        {
          node = node->next;
          continue;
        }
      if (node->info.spec.flag & PT_SPEC_FLAG_DELETE)
        {
          flat = node->info.spec.flat_entity_list;
          if (flat)
        {
          if (flat->info.name.virt_object)
            {
              has_virt_obj = true;
            }
          class_obj = flat->info.name.db_object;
        }
          else
        {
          class_obj = NULL;
        }
          err = sm_class_has_triggers (class_obj, &has_trigger, TR_EVENT_DELETE);

          if (err == NO_ERROR)
        {
          if (has_trigger)
            {
              has_any_delete_trigger = has_trigger;
            }
          else if (!has_any_delete_trigger)
            {
              /* Check for statement delete triggerrs. */
              err = sm_class_has_triggers (class_obj, &has_any_delete_trigger, TR_EVENT_STATEMENT_DELETE);
            }
        }
        }

      node = node->next;
    }

      AU_RESTORE (au_save);
      /* err = has_proxy_trigger(flat, &has_trigger); */
      if (err != NO_ERROR)
    {
      PT_INTERNAL_ERROR (parser, "delete");
      break;        /* stop while loop if error */
    }
      /* sm_class_has_triggers() checked if the class has active triggers */
      statement->info.delete_.has_trigger = has_trigger;

      /* determine whether it can be server-side or OID list deletion */
      server_delete = (!has_trigger && !has_virt_obj);

      statement->info.delete_.server_delete = server_delete;
      if (server_delete && !has_any_delete_trigger && !(statement->info.delete_.hint & PT_HINT_USE_SBR))
    {
      statement->info.delete_.execute_with_commit_allowed = 1;
    }

      stream.xasl_id = NULL;
      if (server_delete)
    {
      /* Server-side deletion case: (by requesting server to execute XASL) build DELETE_PROC XASL */

      /* make query string */
      parser->flag.dont_prt_long_string = 1;
      parser->flag.long_string_skipped = 0;
      parser->flag.print_type_ambiguity = 0;
      PT_NODE_PRINT_TO_ALIAS (parser, statement, CUSTOM_PRINT_4_SHA_COMPUTE | PT_PRINT_LOWER);
      contextp->sql_hash_text = (char *) statement->alias_print;
      err =
        SHA1Compute ((unsigned char *) contextp->sql_hash_text, (unsigned) strlen (contextp->sql_hash_text),
             &contextp->sha1);
      if (err != NO_ERROR)
        {
          ASSERT_ERROR ();
          return err;
        }
      parser->flag.dont_prt_long_string = 0;
      if (parser->flag.long_string_skipped || parser->flag.print_type_ambiguity)
        {
          statement->flag.cannot_prepare = 1;
          err = NO_ERROR;
          break;
        }

      /* look up server's XASL cache for this query string and get XASL file id (XASL_ID) returned if found */
      contextp->recompile_xasl = statement->flag.recompile;
      if (statement->flag.recompile == 0)
        {
          err = prepare_query (contextp, &stream);
          if (err != NO_ERROR)
        {
          ASSERT_ERROR_AND_SET (err);
        }
          else if (contextp->recompile_xasl == true)
        {
          /* recompile requested by server */
          if (stream.xasl_id != NULL)
            {
              free_and_init (stream.xasl_id);
            }
        }
        }
      if (stream.xasl_id == NULL && err == NO_ERROR)
        {
          /* cache not found; make XASL from the parse tree including query optimization and plan generation */

          /* mark the beginning of another level of xasl packing */
          pt_enter_packing_buf ();

          /* this prevents authorization checking during generating XASL */
          AU_SAVE_AND_DISABLE (au_save);

          /* pt_to_delete_xasl() will build XASL tree from parse tree */
          contextp->xasl = pt_to_delete_xasl (parser, statement);
          AU_RESTORE (au_save);

          if (contextp->xasl && (err >= NO_ERROR))
        {
          /* convert the created XASL tree to the byte stream for transmission to the server */
          err = xts_map_xasl_to_stream (contextp->xasl, &stream);
          if (err != NO_ERROR)
            {
              PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
            }
        }
          else
        {
          assert (er_errid () != NO_ERROR);
          err = er_errid ();
          pt_record_error (parser, parser->statement_number, statement->line_number, statement->column_number,
                   er_msg (), NULL);
        }

          /* request the server to prepare the query; give XASL stream generated from the parse tree and get XASL
           * file id returned */
          if (stream.buffer && (err >= NO_ERROR))
        {
          err = prepare_query (contextp, &stream);
          if (err != NO_ERROR)
            {
              assert (er_errid () != NO_ERROR);
              err = er_errid ();
            }
        }

          /* mark the end of another level of xasl packing */
          pt_exit_packing_buf ();

          /* As a result of query preparation of the server, the XASL cache for this query will be created or
           * updated. */

          /* free 'stream' that is allocated inside of xts_map_xasl_to_stream() */
          if (stream.buffer)
        {
          free_and_init (stream.buffer);
        }
          statement->flag.use_plan_cache = 0;
        }
      else
        {
          if (err == NO_ERROR)
        {
          statement->flag.use_plan_cache = 1;
        }
          else
        {
          statement->flag.use_plan_cache = 0;
        }
        }
    }
      else
    {
      /* OID list deletion case: (by selecting OIDs to delete) make SELECT statement for this DELETE statement */

      PT_NODE *select_statement;
      PT_DELETE_INFO *delete_info;

      delete_info = &statement->info.delete_;

      select_statement =
        pt_to_upd_del_query (parser, NULL, NULL, delete_info->spec, delete_info->with, delete_info->class_specs,
                 delete_info->search_cond, delete_info->using_index, NULL, NULL, 0, S_DELETE);
      err = pt_copy_upddel_hints_to_select (parser, statement, select_statement);
      if (err != NO_ERROR)
        {
          parser_free_tree (parser, select_statement);
          break;
        }
      /* translate views or virtual classes into base classes; If we are updating a proxy, the SELECT is not yet
       * fully translated. If we are updating anything else, this is a no-op. */

      /* this prevents authorization checking during view transformation */
      AU_SAVE_AND_DISABLE (au_save);

      select_statement = mq_translate (parser, select_statement);
      AU_RESTORE (au_save);
      if (select_statement)
        {
          /* get XASL_ID by calling do_prepare_select() */
          err = do_prepare_select (parser, select_statement);
          stream.xasl_id = select_statement->xasl_id;
          select_statement->xasl_id = NULL;
          parser_free_tree (parser, select_statement);
        }
      else
        {
          PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
          err = er_errid ();
        }
    }

      if (statement->info.delete_.del_stmt_list != NULL)
    {
      err = do_prepare_delete (parser, statement->info.delete_.del_stmt_list, statement);
      if (err != NO_ERROR)
        {
          break;
        }
    }

      /* save the XASL_ID that is allocated and returned by prepare_query() into 'statement->xasl_id' to be used by
       * do_execute_delete() */
      statement->xasl_id = stream.xasl_id;
    }

  /* if something failed or cannot be prepared in in del_stmt_list clear all statement->xasl_id */
  if (err != NO_ERROR || (statement != NULL && statement->flag.cannot_prepare == 1))
    {
      for (node = save_stmt; node != statement; node = node->next)
    {
      pt_free_statement_xasl_id (node);
    }
      if (err == NO_ERROR && parent != NULL)
    {
      /* set cannot_prepare to parent */
      parent->flag.cannot_prepare = 1;
    }
    }

  return err;
}

/*
 * do_execute_delete() - Execute the prepared DELETE statement
 *   return: Tuple count if success, otherwise an error code
 *   parser(in): Parser context
 *   statement(in): Delete statement
 */
int
do_execute_delete (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  INT64 err, result = 0;
  PT_NODE *flat, *node;
  const char *savepoint_name = NULL;
  DB_OBJECT *class_obj;
  QFILE_LIST_ID *list_id;
  int au_save;
  int wait_msecs = -2, old_wait_msecs = -2;
  float hint_waitsecs;
  PT_NODE *hint_arg;
  int query_flag;
  QUERY_ID query_id_self = parser->query_id;
  bool savepoint_started = false;

  assert (parser->query_id == NULL_QUERY_ID);

  CHECK_MODIFICATION_ERROR ();

  /* savepoint for statement atomicity */
  if ((statement != NULL && statement->next != NULL)
      || (TM_TRAN_ISOLATION () >= TRAN_REP_READ && !statement->info.delete_.server_delete))
    {
      savepoint_name = mq_generate_name (parser, "UmdsP", &delete_savepoint_number);
      if (savepoint_name == NULL)
    {
      return ER_GENERIC_ERROR;
    }
      err = tran_system_savepoint (savepoint_name);
      if (err != NO_ERROR)
    {
      return err;
    }

      savepoint_started = true;
    }

  for (err = NO_ERROR, result = 0; statement && (err >= NO_ERROR); statement = statement->next)
    {
#if defined(ENABLE_UNUSED_FUNCTION)
      /* Delete object case: delete object by OID */

      if (!statement->info.delete_.spec)
    {
      err = delete_object_by_oid (parser, statement);
      continue;     /* continue to next DELETE statement */
    }
#endif /* ENABLE_UNUSED_FUNCTION */

      /* check if it is not necessary to execute this statement, e.g. false where or not prepared correctly */
      if (!statement->xasl_id)
    {
      statement->etc = NULL;
      err = NO_ERROR;
      continue;     /* continue to next DELETE statement */
    }

      /* Server-side deletion or OID list deletion case: execute the prepared(stored) XASL (DELETE_PROC or SELECT
       * statement) */

      /* Request that the server executes the stored XASL, which is the execution plan of the prepared query, with the
       * host variables given by users as parameter values for the query. As a result, query id and result file id
       * (QFILE_LIST_ID) will be returned. do_prepare_delete() has saved the XASL file id (XASL_ID) in
       * 'statement->xasl_id' */

      query_flag = DEFAULT_EXEC_MODE;
      query_flag |= NOT_FROM_RESULT_CACHE;
      query_flag |= RESULT_CACHE_INHIBITED;

      if (parser->flag.is_xasl_pinned_reference)
    {
      query_flag |= XASL_CACHE_PINNED_REFERENCE;
    }

      if (statement->flag.use_auto_commit)
    {
      query_flag |= EXECUTE_QUERY_WITH_COMMIT;
    }

      if (parser->flag.is_auto_commit)
    {
      query_flag |= TRAN_AUTO_COMMIT;
    }

      if (prm_get_bool_value (PRM_ID_QUERY_TRACE) == true && parser->query_trace == true)
    {
      do_set_trace_to_query_flag (&query_flag);
      do_send_plan_trace_to_session (parser);
    }

      // When a transaction is under auto-commit mode, flush all dirty objects to server.
      // Otherwise, flush associated objects.
      if (statement->flag.use_auto_commit)
    {
      err = tran_flush_to_commit ();
    }
      else
    {
      for (node = statement->info.delete_.spec; node && err == NO_ERROR; node = node->next)
        {
          flat = node->info.spec.flat_entity_list;
          if (flat != NULL)
        {
          /* flush necessary objects before execute */
          err = sm_flush_objects (flat->info.name.db_object);
        }
        }
    }

      if (err != NO_ERROR)
    {
      // flush error
      break;
    }

      AU_SAVE_AND_ENABLE (au_save); /* this insures authorization checking for method */

      assert (parser->query_id == NULL_QUERY_ID);
      list_id = NULL;
      err = execute_query (statement->xasl_id, &parser->query_id, parser->host_var_count + parser->auto_param_count,
               parser->host_variables, &list_id, query_flag, NULL, NULL);

      AU_RESTORE (au_save);

      /* in the case of OID list deletion, now delete the selected OIDs */
      if ((err >= NO_ERROR) && list_id)
    {
      if (statement->info.delete_.server_delete)
        {
          err = list_id->tuple_cnt;
        }
      else
        {
          hint_arg = statement->info.delete_.waitsecs_hint;
          if (statement->info.delete_.hint & PT_HINT_LK_TIMEOUT && PT_IS_HINT_NODE (hint_arg))
        {
          hint_waitsecs = (float) atof (hint_arg->info.name.original);
          if (hint_waitsecs > 0)
            {
              wait_msecs = (int) (hint_waitsecs * 1000);
            }
          else
            {
              wait_msecs = (int) hint_waitsecs;
            }
          if (wait_msecs >= -1)
            {
              old_wait_msecs = TM_TRAN_WAIT_MSECS ();
              (void) tran_reset_wait_times (wait_msecs);
            }
        }
          AU_SAVE_AND_DISABLE (au_save);    /* this prevents authorization checking during execution */
          /* delete each oid */
          err = delete_list_by_oids (parser, statement, list_id, savepoint_started);
          AU_RESTORE (au_save);
          if (old_wait_msecs >= -1)
        {
          (void) tran_reset_wait_times (old_wait_msecs);
        }
        }
    }

      /* free returned QFILE_LIST_ID */
      if (list_id)
    {
      if (list_id->tuple_cnt > 0 && statement->info.delete_.server_delete)
        {
          int err2 = NO_ERROR;
          node = statement->info.delete_.spec;

          while (node && err2 >= NO_ERROR)
        {
          if (node->info.spec.flag & PT_SPEC_FLAG_DELETE)
            {
              flat = node->info.spec.flat_entity_list;
              class_obj = (flat) ? flat->info.name.db_object : NULL;

              if (statement->flag.use_auto_commit && tran_was_latest_query_committed ())
            {
              /* Nothing to flush. Avoids flush, since may fetch the class. */
              err2 = sm_decache_instances_after_query_executed_with_commit (class_obj);
            }
              else
            {
              err2 = sm_flush_and_decache_objects (class_obj, true);
            }
            }

          node = node->next;
        }
          if (err2 != NO_ERROR)
        {
          err = err2;
        }
        }
      cursor_free_self_list_id (list_id);
    }

      /* end the query; reset query_id and call qmgr_end_query() */
      pt_end_query (parser, query_id_self);

      /* accumulate intermediate results */
      if (err >= NO_ERROR)
    {
      result += err;
    }

      node = statement->info.delete_.spec;

      while (node && err >= NO_ERROR)
    {
      if (node->info.spec.flag & PT_SPEC_FLAG_DELETE)
        {
          flat = node->info.spec.flat_entity_list;
          class_obj = (flat) ? flat->info.name.db_object : NULL;

          if (class_obj && (statement->flag.use_auto_commit == false || !tran_was_latest_query_committed ()))
        {
          err = db_is_vclass (class_obj);
          if (err > 0)
            {
              err = sm_flush_objects (class_obj);
            }
        }
        }

      node = node->next;
    }
    }

  /* If error and a savepoint was created, rollback to savepoint. No need to rollback if the TM aborted the
   * transaction. */
  if (err < NO_ERROR && savepoint_name && err != ER_LK_UNILATERALLY_ABORTED)
    {
      db_abort_to_savepoint (savepoint_name);
    }

  return (err < NO_ERROR) ? err : result;
}


/*
 * Function Group:
 * Implements the evaluate statement.
 *
 */


/*
 * do_evaluate() - Evaluates an expression
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in/out): Parse tree of a insert statement
 */
int
do_evaluate (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  DB_VALUE expr_value, *into_val;
  PT_NODE *expr, *into_var;
  const char *into_label;

  db_make_null (&expr_value);

  if (!statement || !((expr = statement->info.evaluate.expression)))
    {
      return ER_GENERIC_ERROR;
    }

  pt_evaluate_tree (parser, expr, &expr_value, 1);
  if (pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_SEMANTIC);
      return ER_PT_SEMANTIC;
    }

  statement->etc = (void *) db_value_copy (&expr_value);
  into_var = statement->info.evaluate.into_var;

  if (into_var && into_var->node_type == PT_NAME && (into_label = into_var->info.name.original) != NULL)
    {
      /* create another DB_VALUE of the new instance for the label_table */
      into_val = db_value_copy (&expr_value);

      /* enter {label, ins_val} pair into the label_table */
      error = pt_associate_label_with_value_check_reference (into_label, into_val);
    }

  pr_clear_value (&expr_value);
  return error;
}





/*
 * Function Group:
 * DO functions for insert statements
 *
 */


typedef enum
{
  INSERT_SELECT = 1,
  INSERT_VALUES = 2,
#if defined(ENABLE_UNUSED_FUNCTION)
  /*
   * NOT USED ANY MORE.
   * prm_insert_mode_upper is still left as 31 for backward compatibility.
   *
   */
  INSERT_DEFAULT = 4,
  INSERT_REPLACE = 8,
  INSERT_ON_DUP_KEY_UPDATE = 16
#endif /* ENABLE_UNUSED_FUNCTION */
} SERVER_PREFERENCE;

typedef struct odku_tuple_value_arg ODKU_TUPLE_VALUE_ARG;
struct odku_tuple_value_arg
{
  PT_NODE *insert_stmt;     /* insert statement */
  CURSOR_ID *cursor_p;      /* select cursor id */
};

/* used to generate unique savepoint names */
static int insert_savepoint_number = 0;

static int insert_object_attr (const PARSER_CONTEXT * parser, DB_OTMPL * otemplate, DB_VALUE * value, PT_NODE * name,
                   DB_ATTDESC * attr_desc);
static int check_for_cons (PARSER_CONTEXT * parser, int *has_unique, PT_NODE ** non_null_attrs,
               const PT_NODE * attr_list, DB_OBJECT * class_obj);
static int is_server_insert_allowed (PARSER_CONTEXT * parser, PT_NODE * statement);
static int do_insert_at_server (PARSER_CONTEXT * parser, PT_NODE * statement);
static int insert_subquery_results (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * values_list,
                    PT_NODE * class_, const char **savepoint_name);
static int is_attr_not_in_insert_list (const PARSER_CONTEXT * parser, PT_NODE * name_list, const char *name);
static int check_missing_non_null_attrs (const PARSER_CONTEXT * parser, const PT_NODE * spec, PT_NODE * attr_list,
                     const bool has_default_values_list);
static PT_NODE *make_vmops (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *test_check_option (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static int insert_local (PARSER_CONTEXT * parser, PT_NODE * statement);
static PT_NODE *do_create_odku_stmt (PARSER_CONTEXT * parser, PT_NODE * insert);
static int do_find_unique_constraint_violations (DB_OTMPL * tmpl, bool for_update, OID ** oids, int *oids_count);
static int do_create_midxkey_for_constraint (DB_OTMPL * tmpl, SM_CLASS_CONSTRAINT * constraint, DB_VALUE * key);
static int do_on_duplicate_key_update (PARSER_CONTEXT * parser, DB_OTMPL * tpl, PT_NODE * update_stmt);
static int do_replace_into (PARSER_CONTEXT * parser, DB_OTMPL * tmpl, PT_NODE * spec, PT_NODE * class_specs);
static int is_replace_or_odku_allowed (DB_OBJECT * obj, int *allowed);
static PT_NODE *pt_append_odku_references (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *do_check_insert_server_allowed (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk);
static PT_NODE *do_set_insert_server_not_allowed (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg,
                          int *continue_walk);
/*
 * insert_object_attr()
 *   return: Error code if db_put fails
 *   parser(in): Short description of the param1
 *   otemplate(in/out): Short description of the param2
 *   value(in/out): New attr value
 *   name(in): Name to update
 *   attr_desc(in): Attr descriptor of attribute to update
 */
static int
insert_object_attr (const PARSER_CONTEXT * parser, DB_OTMPL * otemplate, DB_VALUE * value, PT_NODE * name,
            DB_ATTDESC * attr_desc)
{
  int error = NO_ERROR;

  if (DB_VALUE_TYPE (value) == DB_TYPE_OBJECT && !DB_IS_NULL (value))
    {
      /* we may need to check for value coming in from a parameter or host variable as a
       * not-yet-translated-to-real-class value. This must be done at run time in general. */
      db_make_object (value, db_real_instance (db_get_object (value)));
    }

  if (name->info.name.db_object)
    {
      error = db_is_vclass (name->info.name.db_object);
    }
  if (error > 0)
    {
      /* this is a shared attribute of a view. this means this cannot be updated in the template for this real class.
       * Its simply done separately by a db_put. */
      error = obj_set_shared (name->info.name.db_object, name->info.name.original, value);
    }
  else if (!error)
    {
      /* the normal case */
      SM_ATTRIBUTE *att;

      att = db_get_attribute (otemplate->classobj, name->info.name.original);
      error = dbt_dput_internal (otemplate, attr_desc, value);
    }

  return error;
}


/*
 * do_prepare_insert_internal () - Prepares insert statement for server
 *                 execution.
 *
 * return     : Error code.
 * parser (in)    : Parser context.
 * statement (in) : Parse tree node for insert statement.
 */
static int
do_prepare_insert_internal (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  PT_NODE *val = NULL, *head = NULL, *prev = NULL;
  PT_NODE *value_list = NULL;

  COMPILE_CONTEXT *contextp;
  XASL_STREAM stream;

  contextp = &parser->context;

  init_xasl_stream (&stream);

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

  contextp->sql_user_text = statement->sql_user_text;
  contextp->sql_user_text_len = statement->sql_user_text_len;

  /* insert value auto parameterize */
  for (value_list = statement->info.insert.value_clauses; value_list != NULL; value_list = value_list->next)
    {
      head = NULL;
      prev = NULL;
      for (val = value_list->info.node_list.list; val != NULL; val = val->next)
    {
      if (pt_is_const_not_hostvar (val) && !PT_IS_NULL_NODE (val))
        {
          val = pt_rewrite_to_auto_param (parser, val);
          if (prev != NULL)
        {
          prev->next = val;
        }

          if (val == NULL)
        {
          break;
        }
        }

      if (head == NULL)
        {
          head = val;
        }

      prev = val;
    }
      value_list->info.node_list.list = head;
    }

  /* make query string */
  parser->flag.dont_prt_long_string = 1;
  parser->flag.long_string_skipped = 0;
  parser->flag.print_type_ambiguity = 0;
  PT_NODE_PRINT_TO_ALIAS (parser, statement, CUSTOM_PRINT_4_SHA_COMPUTE | PT_PRINT_LOWER);
  contextp->sql_hash_text = (char *) statement->alias_print;
  error =
    SHA1Compute ((unsigned char *) contextp->sql_hash_text, (unsigned) strlen (contextp->sql_hash_text),
         &contextp->sha1);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      return error;
    }
  parser->flag.dont_prt_long_string = 0;
  if (parser->flag.long_string_skipped || parser->flag.print_type_ambiguity)
    {
      statement->flag.cannot_prepare = 1;
      return NO_ERROR;
    }

  /* look up server's XASL cache for this query string and get XASL file id (XASL_ID) returned if found */
  contextp->recompile_xasl = statement->flag.recompile;
  if (statement->flag.recompile == 0)
    {
      error = prepare_query (contextp, &stream);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR_AND_SET (error);
    }
      else if (contextp->recompile_xasl == true)
    {
      /* recompile requested by server */
      if (stream.xasl_id != NULL)
        {
          free_and_init (stream.xasl_id);
        }
    }
    }

  if (stream.xasl_id == NULL && error == NO_ERROR)
    {
      /* mark the beginning of another level of xasl packing */
      pt_enter_packing_buf ();
      contextp->xasl = pt_to_insert_xasl (parser, statement);

      if (contextp->xasl)
    {
      assert (contextp->xasl->dptr_list == NULL);

      if (error == NO_ERROR)
        {
          error = xts_map_xasl_to_stream (contextp->xasl, &stream);
          if (error != NO_ERROR)
        {
          PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
        }
        }
    }
      else
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
    }

      if (stream.buffer && (error >= NO_ERROR))
    {
      error = prepare_query (contextp, &stream);
      if (error != NO_ERROR)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
        }
    }

      /* mark the end of another level of xasl packing */
      pt_exit_packing_buf ();

      /* As a result of query preparation of the server, the XASL cache for this query will be created or updated. */

      /* free 'stream' that is allocated inside of xts_map_xasl_to_stream() */
      if (stream.buffer)
    {
      free_and_init (stream.buffer);
    }

      statement->flag.use_plan_cache = 0;
    }
  else
    {
      if (error == NO_ERROR)
    {
      statement->flag.use_plan_cache = 1;
    }
      else
    {
      statement->flag.use_plan_cache = 0;
    }
    }

  /* save the XASL_ID that is allocated and returned by prepare_query() into 'statement->xasl_id' to be used by
   * do_execute_update() */
  statement->xasl_id = stream.xasl_id;

  return NO_ERROR;
}

/*
 * do_insert_at_server () - Builds an xasl tree for a server insert and
 *              executes it.
 *
 * return     : Error code.
 * parser (in)    : Parser context.
 * statement (in) : Parse tree node for insert statement.
 *
 * NOTE:
 *  The xasl tree has an INSERT_PROC node as the top node and
 *  a BUILDLIST_PROC as its aptr.  The BUILDLIST_PROC selects the
 *  insert values.  The INSERT_PROC node scans the BUILDLIST_PROC results.
 *  The server executes the aptr and then for each instance selected,
 *  inserts it.  The result information is sent back to the
 *  client as a list file without any pages.  The list file tuple count
 *  is used as the return value from this routine.

 *  The instances for the class are flushed from the client before the
 *  insert is executed.
 */
static int
do_insert_at_server (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  XASL_NODE *xasl = NULL;
  INT64 count = 0;
  QUERY_ID query_id_self = parser->query_id;
  QFILE_LIST_ID *list_id = NULL;

  XASL_STREAM stream;

  assert (parser->query_id == NULL_QUERY_ID);

  init_xasl_stream (&stream);

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

  /* mark the beginning of another level of xasl packing */
  pt_enter_packing_buf ();

  xasl = pt_to_insert_xasl (parser, statement);

  if (xasl)
    {
      error = xts_map_xasl_to_stream (xasl, &stream);
      if (error != NO_ERROR)
    {
      PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
    }
    }
  else
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
    }

  if (error == NO_ERROR && stream.buffer != NULL)
    {
      int au_save;
      QUERY_FLAG query_flag;

      query_flag = DEFAULT_EXEC_MODE;
      /* Do not update LAST_INSERT_ID during executing a trigger. */
      if (do_Trigger_involved == true)
    {
      query_flag |= TRIGGER_IS_INVOLVED;
    }

      if (parser->flag.is_auto_commit)
    {
      query_flag |= TRAN_AUTO_COMMIT;
    }

      assert (stream.buffer_size > 0);

      AU_SAVE_AND_ENABLE (au_save); /* this insures authorization checking for method */

      error =
    prepare_and_execute_query (stream.buffer, stream.buffer_size, &parser->query_id,
                   (parser->host_var_count + parser->auto_param_count), parser->host_variables,
                   &list_id, query_flag);
      AU_RESTORE (au_save);
    }

  /* free 'stream' that is allocated inside of xts_map_xasl_to_stream() */
  if (stream.buffer)
    {
      free_and_init (stream.buffer);
    }

  if (list_id)
    {
      count = list_id->tuple_cnt;
      if (count > 0 && (statement->info.insert.odku_assignments != NULL || statement->info.insert.do_replace))
    {
      MOP class_obj = statement->info.insert.spec->info.spec.flat_entity_list->info.name.db_object;

      if (statement->flag.use_auto_commit && tran_was_latest_query_committed ())
        {
          /* Nothing to flush. Avoids flush, since may fetch the class. */
          error = sm_decache_instances_after_query_executed_with_commit (class_obj);
        }
      else
        {
          error = sm_flush_and_decache_objects (class_obj, true);
        }
    }

      if (parser->flag.return_generated_keys)
    {
      statement->etc = (void *) list_id;
    }
      else
    {
      cursor_free_self_list_id (list_id);
    }
    }

  pt_end_query (parser, query_id_self);

  /* mark the end of another level of xasl packing */
  pt_exit_packing_buf ();

  if (error >= NO_ERROR)
    {
      return count;
    }
  else
    {
      return error;
    }
}

/*
 * check_for_cons() - Determines whether an attribute has not null or unique
 *            constraints.
 *
 *   return: Error code
 *   parser(in): Parser context
 *   has_unique(in/out):
 *   non_null_attrs(in/out): all the "NOT NULL" attributes except for the
 *                           AUTO_INCREMENT ones
 *   attr_list(in): Parse tree of an insert statement attribute list
 *   class_obj(in): Class object

 */
static int
check_for_cons (PARSER_CONTEXT * parser, int *has_unique, PT_NODE ** non_null_attrs, const PT_NODE * attr_list,
        DB_OBJECT * class_obj)
{
  PT_NODE *pointer;

  assert (non_null_attrs != NULL);
  if (*non_null_attrs != NULL)
    {
      /* non_null_attrs already checked */
      return NO_ERROR;
    }
  *has_unique = 0;

  while (attr_list)
    {
      if (attr_list->node_type != PT_NAME)
    {
      /* bullet proofing, should not get here */
      return ER_GENERIC_ERROR;
    }

      if (*has_unique == 0 && sm_att_unique_constrained (class_obj, attr_list->info.name.original))
    {
      *has_unique = 1;
    }

      if (sm_att_constrained (class_obj, attr_list->info.name.original, SM_ATTFLAG_NON_NULL))
    {
      /* NULL values are allowed for auto_increment columns. It means that the next auto_increment value will be
       * inserted. */
      if (sm_att_auto_increment (class_obj, attr_list->info.name.original) == false)
        {
          pointer = pt_point (parser, attr_list);
          if (pointer == NULL)
        {
          PT_ERRORm (parser, attr_list, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);

          if (*non_null_attrs)
            {
              parser_free_tree (parser, *non_null_attrs);
            }
          *non_null_attrs = NULL;

          return MSGCAT_RUNTIME_RESOURCES_EXHAUSTED;
        }
          *non_null_attrs = parser_append_node (pointer, *non_null_attrs);
        }
    }

      attr_list = attr_list->next;
    }

  if (*has_unique == 0)
    {
      /* check if the class has an auto-increment attribute which has unique constraint */

      SM_CLASS *class_;
      SM_ATTRIBUTE *att;

      if (au_fetch_class_force (class_obj, &class_, AU_FETCH_READ) == NO_ERROR)
    {
      for (att = class_->ordered_attributes; att; att = att->order_link)
        {
          if ((att->flags & SM_ATTFLAG_AUTO_INCREMENT) && classobj_has_unique_constraint (att->constraints))
        {
          *has_unique = 1;
          break;
        }
        }
    }
    }

  return NO_ERROR;
}

/*
 * is_server_insert_allowed () - Checks to see if a server-side insert is
 *                               allowed
 *
 * return     : Error code.
 * parser (in)    : Parser context.
 * statement (in) : Parse tree node for insert statement.
 */
static int
is_server_insert_allowed (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  int trigger_involved;
  PT_NODE *attrs = NULL, *attr = NULL;
  PT_NODE *value_clauses = NULL, *class_ = NULL;
  /* set lock timeout hint if specified */
  int server_preference;
  int save_au;

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

  if (statement->info.insert.server_allowed != SERVER_INSERT_NOT_CHECKED)
    {
      /* already checked */
      return NO_ERROR;
    }
  statement->info.insert.server_allowed = SERVER_INSERT_IS_NOT_ALLOWED;

  AU_DISABLE (save_au);

  class_ = statement->info.insert.spec->info.spec.flat_entity_list;

  value_clauses = statement->info.insert.value_clauses;
  attrs = statement->info.insert.attr_list;
  /* server insert cannot handle insert into a shared attribute */
  attr = attrs;
  while (attr)
    {
      if (attr->node_type != PT_NAME)
    {
      /* this can occur when inserting into a view with default values. The name list may not be inverted, and may
       * contain expressions, such as (x+2). */
      goto end;
    }
      if (attr->info.name.meta_class != PT_NORMAL)
    {
      /* We found a shared attribute, bail out */
      goto end;
    }
      attr = attr->next;
    }

  error =
    check_for_cons (parser, &statement->info.insert.has_uniques, &statement->info.insert.non_null_attrs, attrs,
            class_->info.name.db_object);
  if (error != NO_ERROR)
    {
      goto end;
    }

  server_preference = prm_get_integer_value (PRM_ID_INSERT_MODE);

  if (statement->info.insert.hint & PT_HINT_INSERT_MODE)
    {
      PT_NODE *mode = statement->info.insert.insert_mode;
      if (mode && mode->node_type == PT_NAME)
    {
      server_preference = atoi (mode->info.name.original);
    }
    }

  if (value_clauses->info.node_list.list_type == PT_IS_SUBQUERY)
    {
      if (!(server_preference & INSERT_SELECT))
    {
      goto end;
    }
    }
  else if (value_clauses->info.node_list.list_type == PT_IS_VALUE)
    {
      int server_allowed = 1;
      if (!(server_preference & INSERT_VALUES))
    {
      goto end;
    }
      (void) parser_walk_tree (parser, value_clauses, do_check_insert_server_allowed, &server_allowed, NULL, NULL);
      if (pt_has_error (parser))
    {
      error = ER_FAILED;
      goto end;
    }
      if (!server_allowed)
    {
      goto end;
    }
    }
  else
    {
      assert (false);
    }

  if (statement->info.insert.into_var != NULL)
    {
      goto end;
    }

  /* check option could be done on the server by adding another predicate to the insert_info block. However, one must
   * also take care that subqueries in this predicate have their xasl blocks properly attached to the insert xasl
   * block. Currently, pt_to_pred will do that only if being called from parser_generate_xasl. This may mean that
   * do_server_insert should call parser_generate_xasl, and have a portion of its code put. */
  if (statement->info.insert.where != NULL)
    {
      goto end;
    }

  if (statement->info.insert.do_replace && statement->info.insert.has_uniques)
    {
      error = sm_class_has_triggers (class_->info.name.db_object, &trigger_involved, TR_EVENT_DELETE);
      if (error != NO_ERROR)
    {
      goto end;
    }

      if (trigger_involved)
    {
      goto end;
    }
    }

  if (statement->info.insert.odku_assignments != NULL && statement->info.insert.has_uniques)
    {
      error = sm_class_has_triggers (class_->info.name.db_object, &trigger_involved, TR_EVENT_UPDATE);
      if (error != NO_ERROR)
    {
      goto end;
    }

      if (trigger_involved)
    {
      goto end;
    }
    }

  error = sm_class_has_triggers (class_->info.name.db_object, &trigger_involved, TR_EVENT_INSERT);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* Even if unique indexes are defined on the class, the operation could be performed on server. */
  if (!trigger_involved && !do_Trigger_involved)
    {
      statement->info.insert.server_allowed = SERVER_INSERT_IS_ALLOWED;
    }

end:
  /* the entire insert statement must be executed on client or on server. if any sub-insert is not allowed on server,
   * or if the root is not allowed on server, all inserts must be executed on client */
  if (error == NO_ERROR && statement->info.insert.server_allowed == SERVER_INSERT_IS_NOT_ALLOWED
      && value_clauses->info.node_list.list_type == PT_IS_VALUE)
    {
      (void) parser_walk_tree (parser, value_clauses, do_set_insert_server_not_allowed, NULL, NULL, NULL);
      if (pt_has_error (parser))
    {
      error = ER_FAILED;
    }
    }
  AU_ENABLE (save_au);
  return error;
}

/*
 * do_check_insert_server_allowed () - Parser walk function that checks all
 *                     sub-inserts are allowed on server.
 *
 * return         : Unchanged node argument.
 * parser (in)        : Parser context.
 * node (in)          : Parse tree node.
 * arg (out)          : int * argument that stores server_allowed.
 * continue_walk (in) : Continue walk.
 */
static PT_NODE *
do_check_insert_server_allowed (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  int *server_allowed = (int *) arg;
  int error = NO_ERROR;

  if (node == NULL || node->node_type != PT_INSERT)
    {
      /* stop check */
      return node;
    }

  *server_allowed = 1;
  error = is_server_insert_allowed (parser, node);
  if (error != NO_ERROR || node->info.insert.server_allowed != SERVER_INSERT_IS_ALLOWED)
    {
      *server_allowed = 0;
      *continue_walk = PT_STOP_WALK;
    }
  return node;
}

/*
 * do_set_insert_server_not_allowed () - Parser walk function that should be
 *                   called when top insert is not allowed
 *                   on server. This will force all sub
 *                   inserts to run on client.
 *
 * return         : Unchanged node argument.
 * parser (in)        : Parser context.
 * node (in)          : Parse tree node.
 * void_arg (in)      : Void argument.
 * continue_walk (in) : Continue walk.
 */
static PT_NODE *
do_set_insert_server_not_allowed (PARSER_CONTEXT * parser, PT_NODE * node, void *void_arg, int *continue_walk)
{
  int error = NO_ERROR;

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

  if (node->info.insert.server_allowed == SERVER_INSERT_NOT_CHECKED)
    {
      /* is_server_insert_allowed was no called for this insert node, must call check_for_cons (). */
      DB_OBJECT *class_obj = node->info.insert.spec->info.spec.flat_entity_list->info.name.db_object;
      error =
    check_for_cons (parser, &node->info.insert.has_uniques, &node->info.insert.non_null_attrs,
            node->info.insert.attr_list, class_obj);
      if (error != NO_ERROR)
    {
      *continue_walk = PT_STOP_WALK;
      return node;
    }
    }
  node->info.insert.server_allowed = SERVER_INSERT_IS_NOT_ALLOWED;
  return node;
}

/*
 * do_create_midxkey_for_constraint () - create a MIDX_KEY db_value for the
 *                   specified constraint using the values
 *                   assigned in an object template
 * return : error code or NO_ERROR;
 * tmpl (in)       : object template
 * constraint (in) : constraint
 * key (in/out)    : the MIDX key
 */
static int
do_create_midxkey_for_constraint (DB_OTMPL * tmpl, SM_CLASS_CONSTRAINT * constraint, DB_VALUE * key)
{
  DB_MIDXKEY midxkey;
  SM_ATTRIBUTE **attr = NULL;
  int buf_size = 0, i, error = NO_ERROR, attr_count = 0;
  unsigned char *bits;
  char *nullmap_ptr = NULL;
  OR_BUF buf;
  DB_VALUE *val = NULL;
  TP_DOMAIN *attr_dom = NULL, *dom = NULL, *setdomain = NULL;
  const int *asc_desc = NULL;

  midxkey.buf = NULL;
  midxkey.min_max_val.position = -1;
  asc_desc = db_constraint_asc_desc (constraint);

  /* compute key size */
  for (attr_count = 0, attr = constraint->attributes; *attr != NULL; attr_count++, attr++)
    {
      val = NULL;
      if (tmpl->assignments[(*attr)->order] != NULL)
    {
      val = tmpl->assignments[(*attr)->order]->variable;
    }

      attr_dom = tp_domain_copy ((*attr)->domain, false);
      if (attr_dom == NULL)
    {
      error = ER_FAILED;
      goto error_return;
    }

      if (asc_desc != NULL && asc_desc[attr_count] == 1)
    {
      attr_dom->is_desc = 1;
    }

      if (val != NULL && !DB_IS_NULL (val))
    {
      buf_size += attr_dom->type->get_index_size_of_value (val);
    }

      if (setdomain == NULL)
    {
      setdomain = attr_dom;
    }
      else
    {
      dom->next = attr_dom;
    }
      dom = attr_dom;
    }

  buf_size += or_multi_header_size (attr_count);

  midxkey.buf = (char *) db_private_alloc (NULL, buf_size);
  if (midxkey.buf == NULL)
    {
      error = ER_FAILED;
      goto error_return;
    }

  or_init (&buf, midxkey.buf, buf_size);

  nullmap_ptr = midxkey.buf;
  or_multi_clear_header (nullmap_ptr, attr_count);

  or_advance (&buf, or_multi_header_size (attr_count));

  for (i = 0, attr = constraint->attributes; *attr != NULL; attr++, i++)
    {
      val = NULL;
      if (tmpl->assignments[(*attr)->order] != NULL)
    {
      val = tmpl->assignments[(*attr)->order]->variable;
    }
      dom = (*attr)->domain;

      or_multi_put_element_offset (nullmap_ptr, attr_count, CAST_BUFLEN (buf.ptr - buf.buffer), i);

      if (!DB_IS_NULL (val))
    {
      dom->type->index_writeval (&buf, val);
      or_multi_set_not_null (nullmap_ptr, i);
    }
      else
    {
      assert (or_multi_is_null (nullmap_ptr, i));
    }
    }

  or_multi_put_size_offset (nullmap_ptr, attr_count, buf_size);

  midxkey.size = buf_size;
  midxkey.ncolumns = attr_count;
  midxkey.domain = tp_domain_construct (DB_TYPE_MIDXKEY, NULL, attr_count, 0, setdomain);
  if (midxkey.domain == NULL)
    {
      error = ER_FAILED;
      goto error_return;
    }
  midxkey.domain = tp_domain_cache (midxkey.domain);
  midxkey.min_max_val.position = -1;
  midxkey.min_max_val.type = MIN_COLUMN;

  error = db_make_midxkey (key, &midxkey);
  if (error != NO_ERROR)
    {
      goto error_return;
    }
  key->need_clear = true;
  return NO_ERROR;

error_return:
  if (midxkey.buf != NULL)
    {
      db_private_free (NULL, midxkey.buf);
    }
  dom = setdomain;
  while (dom != NULL)
    {
      attr_dom = dom->next;
      dom->next = NULL;
      tp_domain_free (dom);
      dom = attr_dom;
    }
  return error;
}

/*
 * do_create_odku_stmt () - create an UPDATE statement for ON DUPLICATE KEY
 *              UPDATE node
 * return : update node or NULL
 * parser (in) : parser context
 * insert (in) : INSERT statement node
 *
 * Note: this function alters the flag set on the SPEC node of the INSERT
 *   statement. Callers should backup the value of the flag and restore it
 *   when they're finished with the UPDATE statement.
 */
static PT_NODE *
do_create_odku_stmt (PARSER_CONTEXT * parser, PT_NODE * insert)
{
  PT_NODE *update = NULL;

  if (insert == NULL || insert->node_type != PT_INSERT)
    {
      assert (false);
      PT_INTERNAL_ERROR (parser, "invalid arguments");
      return NULL;
    }

  insert->info.insert.spec->info.spec.flag =
    (PT_SPEC_FLAG) (insert->info.insert.spec->info.spec.flag | PT_SPEC_FLAG_UPDATE);

  update = parser_new_node (parser, PT_UPDATE);
  if (update == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      goto error_return;
    }
  update->info.update.assignment = insert->info.insert.odku_assignments;
  update->info.update.spec = insert->info.insert.spec;

  if (insert->info.insert.where != NULL)
    {
      PT_NODE *check_where = parser_new_node (parser, PT_CHECK_OPTION);
      if (check_where == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      goto error_return;
    }

      check_where->info.check_option.expr = insert->info.insert.where;
      check_where->info.check_option.spec_id = insert->info.insert.spec->info.spec.id;

      update->info.update.check_where = check_where;
    }

  return update;

error_return:
  if (update != NULL)
    {
      update->info.update.assignment = NULL;
      update->info.update.spec = NULL;
      if (update->info.update.check_where != NULL)
    {
      update->info.update.check_where->info.check_option.expr = NULL;
    }
      parser_free_tree (parser, update);
    }

  return NULL;
}

/*
 * do_find_unique_constraint_violations () - find oids for which unique
 *                       constraints  would be violated by
 *                       inserting an object template
 * return : error code or NO_ERROR
 * tmpl (in)       : object template
 * for_update (in) : if true, offending OIDs will be updated
 * oids (in/out)   : array of offending OIDs
 * oids_count (in) : number of OIDs in array
 */
static int
do_find_unique_constraint_violations (DB_OTMPL * tmpl, bool for_update, OID ** oids, int *oids_count)
{
  int error = NO_ERROR;
  SM_CLASS_CONSTRAINT *constraint = NULL;
  SM_CLASS *class_ = NULL;
  int unique_count = 0, key_cnt, i, level, attr_count = 0;
  BTID *unique_btids = NULL;
  DB_VALUE *unique_keys = NULL;
  SM_ATTRIBUTE **attr;
  OBJ_TEMPASSIGN *assignment = NULL;
  bool should_verify = false;
  BTREE_SEARCH result;
  SCAN_OPERATION_TYPE op_type = S_UPDATE;

  qo_get_optimization_param (&level, QO_PARAM_LEVEL);
  if (level & 0x02)
    {
      /* User is only interested in query plan. Query will not be executed and we have no plan to show. */
      *oids = NULL;
      *oids_count = 0;
      return NO_ERROR;
    }
  /* Populate the defaults and auto_increment values here because we need them when building the WHERE clause for the
   * SELECT statement. These values will not be reassigned if the template will eventually be inserted as they are
   * already populated. */
  error = obt_populate_known_arguments (tmpl);
  if (error != NO_ERROR)
    {
      return error;
    }

  class_ = tmpl->class_;
  for (constraint = class_->constraints; constraint != NULL; constraint = constraint->next)
    {
      if (!SM_IS_CONSTRAINT_UNIQUE_FAMILY (constraint->type))
    {
      continue;
    }
      unique_count++;
    }

  if (unique_count == 0)
    {
      /* class does not have unique constraints */
      *oids = NULL;
      oids_count = 0;
      return NO_ERROR;
    }

  unique_btids = (BTID *) db_private_alloc (NULL, unique_count * sizeof (BTID));
  if (unique_btids == NULL)
    {
      error = ER_FAILED;
      goto cleanup;
    }
  unique_keys = (DB_VALUE *) db_private_alloc (NULL, unique_count * sizeof (DB_VALUE));
  if (unique_keys == NULL)
    {
      error = ER_FAILED;
      goto cleanup;
    }
  key_cnt = 0;
  for (constraint = class_->constraints; constraint != NULL; constraint = constraint->next)
    {
      if (!SM_IS_CONSTRAINT_UNIQUE_FAMILY (constraint->type))
    {
      continue;
    }
      BTID_COPY (&unique_btids[key_cnt], &constraint->index_btid);
      db_make_null (&unique_keys[key_cnt]);
      attr_count = 0;
      for (attr = constraint->attributes; *attr != NULL; attr++)
    {
      assignment = tmpl->assignments[(*attr)->order];
      if (assignment != NULL && assignment->variable != NULL)
        {
          /* this constraint may be violated */
          should_verify = true;
        }
      attr_count++;
    }

      if (!should_verify || attr_count == 0)
    {
      continue;
    }

      if (attr_count == 1)
    {
      /* single key handling */
      attr = constraint->attributes;
      assignment = tmpl->assignments[(*attr)->order];
      if (assignment == NULL)
        {
          /* value not set */
          continue;
        }
      pr_clone_value (assignment->variable, &unique_keys[key_cnt]);
      key_cnt++;
      continue;
    }

      /* multiple key, need to create a MIDXKEY */
      error = do_create_midxkey_for_constraint (tmpl, constraint, &unique_keys[key_cnt]);
      if (error != NO_ERROR)
    {
      goto cleanup;
    }
      key_cnt++;
    }

  if (key_cnt == 0)
    {
      goto cleanup;
    }

  if (for_update)
    {
      op_type = S_UPDATE;
    }
  else
    {
      op_type = S_DELETE;
    }

  result =
    btree_find_multi_uniques (ws_oid (tmpl->classobj), tmpl->pruning_type, unique_btids, unique_keys, key_cnt, op_type,
                  oids, oids_count);
  if (result == BTREE_ERROR_OCCURRED)
    {
      error = ER_FAILED;
    }

cleanup:
  if (unique_keys != NULL)
    {
      for (i = 0; i < key_cnt; i++)
    {
      pr_clear_value (&unique_keys[i]);
    }
      db_private_free (NULL, unique_keys);
    }
  if (unique_btids != NULL)
    {
      db_private_free (NULL, unique_btids);
    }
  return error;
}

/*
 * do_on_duplicate_key_update() - runs an update statement instead of an
 *                                insert statement for the cases in which
 *                                the insert would cause a unique constraint
 *                                violation
 *   return: The number of affected rows if successful, error code otherwise
 *
 *   parser(in)       : Parser context
 *   tmpl(in)         : object template to be inserted
 *   update_stmt(in)  : the update statement to run if there are unique
 *          constraint violations
 *
 * Notes: This function creates a SELECT statement based on the values
 *        supplied in the template object and runs update_stmt for one
 *        of the OIDs with which tmpl would generate unique key conflicts.
 *    If this function returns 0 then no rows were updated and the caller
 *    should proceed with the insert.
 */
static int
do_on_duplicate_key_update (PARSER_CONTEXT * parser, DB_OTMPL * tmpl, PT_NODE * update_stmt)
{
  int retval = NO_ERROR;
  int ret_code = 0;
  OID *oids = NULL;
  int oids_count = 0;

  retval = do_find_unique_constraint_violations (tmpl, true, &oids, &oids_count);
  if (retval != NO_ERROR)
    {
      goto cleanup;
    }
  assert_release (oids_count <= 1);
  if (oids_count == 0)
    {
      assert (oids == NULL);
      return NO_ERROR;
    }

  update_stmt->info.update.object = ws_mop (oids, NULL);

  ret_code = update_object_by_oid (parser, update_stmt, ON_DUPLICATE_KEY_UPDATE);
  update_stmt->info.update.object = NULL;
  if (ret_code < NO_ERROR)
    {
      retval = ret_code;
      goto cleanup;
    }

  /* one updated row */
  retval = 1;

cleanup:
  if (oids != NULL)
    {
      free_and_init (oids);
    }
  return retval;
}

/*
 * do_replace_into() - runs an delete statement for the cases in
 *                     which INSERT would cause a unique constraint violation
 *
 *   return: The number of affected rows if successful, error code otherwise
 *
 *   parser(in)       : Parser context
 *   tmpl(in)         : object template to be inserted
 *   spec(in)         : the spec used for insert
 *   class_specs(in)  :
 *
 * Notes: This function creates a SELECT statement based on the values
 *        supplied in the template object and runs delete for the
 *        OIDs with which tmpl would generate unique key violations
 */
static int
do_replace_into (PARSER_CONTEXT * parser, DB_OTMPL * tmpl, PT_NODE * spec, PT_NODE * class_specs)
{
  int retval = 0, error = NO_ERROR;
  OID *oids = NULL;
  int oids_count = 0, i;
  MOP obj = NULL;

  error = do_find_unique_constraint_violations (tmpl, false, &oids, &oids_count);
  if (error != NO_ERROR)
    {
      retval = error;
      goto cleanup;
    }
  if (oids_count == 0)
    {
      retval = 0;
      goto cleanup;
    }

  retval = 0;
  for (i = 0; i < oids_count; i++)
    {
      obj = ws_mop (&oids[i], NULL);
      error = db_is_deleted (obj);
      if (error < 0)
    {
      goto cleanup;
      retval = error;
    }
      if (error > 0)
    {
      error = NO_ERROR;
      continue;
    }

      error = delete_object_tuple (obj);
      if (error != NO_ERROR)
    {
      retval = error;
      goto cleanup;
    }
      retval++;
    }

cleanup:
  if (oids != NULL)
    {
      free_and_init (oids);
    }
  return retval;
}

/*
 * is_replace_or_odku_allowed() - checks to see if the class is partitioned or
 *                part of an inheritance chain
 *
 *   return: error code if unsuccessful, NO_ERROR otherwise
 *
 *   obj(in)          : object to be checked
 *   allowed(out)     : 0 if not allowed, 1 if allowed
 *
 */
static int
is_replace_or_odku_allowed (DB_OBJECT * obj, int *allowed)
{
  int error = NO_ERROR;
  SM_CLASS *smclass = NULL;

  *allowed = 1;
  error = sm_is_partitioned_class (obj);
  if (error < 0)
    {
      return error;
    }
  if (error > 0)
    {
      return NO_ERROR;
    }

  error = au_fetch_class (obj, &smclass, AU_FETCH_READ, AU_SELECT);
  if (error != NO_ERROR)
    {
      return error;
    }

  if (smclass->inheritance != NULL || smclass->users != NULL)
    {
      *allowed = 0;
    }

  return error;
}

/*
 * do_insert_template () - Final step of insert execution on client.
 *             1. Checks if insert is allowed on server. If it is,
 *                calls do_insert_at_server ().
 *             2. If INSERT ... SELECT is not allowed on server,
 *                calls insert_subquery_results ().
 *             3. If INSERT ... VALUES is not allowed on server,
 *                it is handled here.
 *
 * return          : NO_ERROR or error code.
 * parser (in)         : Parser context.
 * otemplate (out)     : Class template to be inserted.
 * statement (in)      : Parse tree of insert statement.
 * savepoint_name (in/out) : Name of savepoint (no savepoint if null).
 * row_count_ptr (in/out)  : Pointer to row counter.
 */
int
do_insert_template (PARSER_CONTEXT * parser, DB_OTMPL ** otemplate, PT_NODE * statement, const char **savepoint_name,
            int *row_count_ptr)
{
  const char *into_label = NULL;
  DB_VALUE *ins_val = NULL, *val = NULL, db_value;
  int error = NO_ERROR, flag = 0;
  PT_NODE *attr = NULL, *vc = NULL;
  PT_NODE *into = NULL;
  PT_NODE *class_ = NULL;
  PT_NODE *update = NULL;
  DB_ATTDESC **attr_descs = NULL;
  int i, degree, row_count = 0;
  int wait_msecs = -2, old_wait_msecs = -2;
  float hint_waitsecs;
  PT_NODE *hint_arg;
  int pruning_type = DB_NOT_PARTITIONED_CLASS;
  PT_NODE *value_clauses = statement->info.insert.value_clauses;
  PT_NODE *value_list = NULL;
  DB_OBJECT *obj = NULL, *vobj = NULL;
  unsigned int save_custom;
  DB_VALUE *value = NULL;
  DB_SEQ *seq = NULL;
  int obj_count = 0;

  assert (otemplate != NULL);
  if (otemplate == NULL)
    {
      return ER_FAILED;
    }
  *otemplate = NULL;

  db_make_null (&db_value);

  if (row_count_ptr != NULL)
    {
      *row_count_ptr = 0;
    }

  degree = 0;
  class_ = statement->info.insert.spec->info.spec.flat_entity_list;
  flag = statement->info.insert.spec->info.spec.flag;
  /* clear any previous error indicator because the rest of do_insert is sensitive to er_errid(). */
  er_clear ();

  error = do_insert_checks (parser, statement, &class_, &update, value_clauses);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto cleanup;
    }

  into = statement->info.insert.into_var;
  if (into != NULL && PT_IS_NAME_NODE (into) && into->info.name.meta_class == PT_PARAMETER)
    {
      assert (into->info.name.original != NULL);
      into_label = into->info.name.original;
    }
  else
    {
      into_label = NULL;
    }
  if (into_label)
    {
      ins_val = db_value_create ();
      if (ins_val == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto cleanup;
    }
      db_make_object (ins_val, (DB_OBJECT *) NULL);
    }

  if (TM_TRAN_ISOLATION () >= TRAN_REP_READ && statement->info.insert.server_allowed != SERVER_INSERT_IS_ALLOWED)
    {
      /* We need to make sure snapshot is generated for this execution. */
      error = log_get_mvcc_snapshot ();
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto cleanup;
    }
    }

  if (statement->info.insert.server_allowed == SERVER_INSERT_IS_ALLOWED)
    {
      if (error != NO_ERROR)
    {
      goto cleanup;
    }
      error = do_insert_at_server (parser, statement);
      if (error >= 0)
    {
      row_count = error;
    }
    }
  else if (value_clauses->info.node_list.list_type == PT_IS_SUBQUERY
       && (vc = value_clauses->info.node_list.list) != NULL)
    {
      /* execute sub-query & insert its results into target class */
      row_count = insert_subquery_results (parser, statement, value_clauses, class_, savepoint_name);
      error = (row_count < 0) ? row_count : NO_ERROR;
      if (error == NO_ERROR)
    {
      if (ins_val != NULL)
        {
          if (row_count > 1)
        {
          error = ER_DO_INSERT_TOO_MANY;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
          goto cleanup;
        }
          val = (DB_VALUE *) statement->etc;
          if (val != NULL)
        {
          db_make_object (ins_val, db_get_object (val));
        }
          if (into_label != NULL)
        {
          error = pt_associate_label_with_value_check_reference (into_label, ins_val);
        }
        }
    }
    }
  else if (value_clauses->info.node_list.list_type == PT_IS_VALUE
       || value_clauses->info.node_list.list_type == PT_IS_DEFAULT_VALUE)
    {
      if (qo_need_skip_execution ())
    {
      return NO_ERROR;
    }

      error = sm_partitioned_class_type (class_->info.name.db_object, &pruning_type, NULL, NULL);
      if (error != NO_ERROR)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto cleanup;
    }

      if (parser->flag.return_generated_keys)
    {
      seq = set_create_sequence (0);
      if (seq == NULL)
        {
          error = ER_FAILED;
          goto cleanup;
        }
    }

      error = do_evaluate_insert_values (parser, statement);
      if (error != NO_ERROR)
    {
      goto cleanup;
    }

      for (value_list = value_clauses; value_list != NULL; value_list = value_list->next)
    {
      /* now create the object using templates, and then dbt_put each value for each corresponding attribute. Of
       * course, it is presumed that the order in which attributes are defined in the class as well as in the
       * actual insert statement is preserved. */
      *otemplate = dbt_create_object_internal (class_->info.name.db_object);
      if (*otemplate == NULL)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
          goto cleanup;
        }
      (*otemplate)->pruning_type = pruning_type;
      if (pruning_type != DB_NOT_PARTITIONED_CLASS)
        {
          /* The reason we're forcing a flush here is to throw an error if the object does belong to any partition.
           * If we don't do it here, the error will be thrown when the object is flushed either by the next
           * statement or by a commit/rollback call. However, there cases when we don't need to do this. Hash
           * partitioning algorithm guarantees that there always is a partition for each record and range
           * partitioning using max value/min value does the same. This flushing should be refined. */
          obt_set_force_flush (*otemplate);
        }

      vc = value_list->info.node_list.list;
      attr = statement->info.insert.attr_list;
      degree = pt_length_of_list (attr);

      /* allocate attribute descriptors */
      if (attr != NULL && attr_descs == NULL)
        {
          attr_descs = (DB_ATTDESC **) calloc (degree, sizeof (DB_ATTDESC *));
          if (attr_descs == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
              (degree * sizeof (DB_ATTDESC *)));
          error = ER_OUT_OF_VIRTUAL_MEMORY;
          goto cleanup;
        }
        }

      hint_arg = statement->info.insert.waitsecs_hint;
      if (statement->info.insert.hint & PT_HINT_LK_TIMEOUT && PT_IS_HINT_NODE (hint_arg))
        {
          hint_waitsecs = (float) atof (hint_arg->info.name.original);
          if (hint_waitsecs > 0)
        {
          wait_msecs = (int) (hint_waitsecs * 1000);
        }
          else
        {
          wait_msecs = (int) hint_waitsecs;
        }
          if (wait_msecs >= -1)
        {
          old_wait_msecs = TM_TRAN_WAIT_MSECS ();

          (void) tran_reset_wait_times (wait_msecs);
        }
        }
      i = 0;
      while (attr && vc)
        {
          assert (vc->node_type == PT_INSERT_VALUE && vc->info.insert_value.is_evaluated);
          if (vc->node_type != PT_INSERT_VALUE || !vc->info.insert_value.is_evaluated)
        {
          error = ER_FAILED;
          goto cleanup;
        }

          /* don't get descriptors for shared attrs of views */
          if (attr_descs[i] == NULL)
        {
          int is_vclass = 0;

          if (attr->info.name.db_object)
            {
              is_vclass = db_is_vclass (attr->info.name.db_object);
              if (is_vclass < 0)
            {
              error = is_vclass;
            }
            }
          if (!is_vclass)
            {
              error =
            db_get_attribute_descriptor (class_->info.name.db_object, attr->info.name.original, 0, 1,
                             &attr_descs[i]);
            }
        }
          if (error >= NO_ERROR)
        {
          error = insert_object_attr (parser, *otemplate, &vc->info.insert_value.value, attr, attr_descs[i]);
        }

          if (!pt_has_error (parser))
        {
          if (error < NO_ERROR)
            {
              save_custom = parser->custom_print;
              parser->custom_print = parser->custom_print | PT_PRINT_DB_VALUE;

              PT_ERRORmf3 (parser, vc, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_DBT_PUT_ERROR,
                   pt_short_print (parser, vc), attr->info.name.original,
                   pt_chop_trailing_dots (parser, db_error_string (3)));

              parser->custom_print = save_custom;

              goto cleanup;
            }
        }
          else
        {
          goto cleanup;
        }

          attr = attr->next;
          vc = vc->next;
          i++;
        }

      /* inserted one more row */
      row_count++;

      if ((*otemplate) != NULL && statement->info.insert.odku_assignments)
        {
          assert (update != NULL);
          error = do_on_duplicate_key_update (parser, *otemplate, update);
          if (error < 0)
        {
          dbt_abort_object (*otemplate);
          *otemplate = NULL;
          goto cleanup;
        }
          else if (error > 0)
        {
          /* a successful update, go to finish */
          row_count += error;
          dbt_abort_object (*otemplate);
          *otemplate = NULL;
          error = NO_ERROR;
        }
          else
        {       /* error == 0 */
          int level;
          qo_get_optimization_param (&level, QO_PARAM_LEVEL);
          if (level & 0x02)
            {
              /* do not execute, go to finish */
              row_count = 0;
              dbt_abort_object (*otemplate);
              *otemplate = NULL;
              error = NO_ERROR;
            }
        }
        }
      if ((*otemplate) != NULL && statement->info.insert.do_replace)
        {
          error =
        do_replace_into (parser, *otemplate, statement->info.insert.spec, statement->info.insert.class_specs);
          if (error > 0)
        {
          row_count += error;
          error = NO_ERROR;
        }
        }

      if (error < NO_ERROR)
        {
          goto cleanup;
        }

      if (ins_val != NULL && *otemplate != NULL)
        {
          if (row_count > 1)
        {
          error = ER_DO_INSERT_TOO_MANY;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
          goto cleanup;
        }
          error = dbt_set_label (*otemplate, ins_val);
          if (error == NO_ERROR)
        {
          if (into_label != NULL)
            {
              error = pt_associate_label_with_value_check_reference (into_label, ins_val);
            }
        }
          if (error != NO_ERROR)
        {
          goto cleanup;
        }
        }

      if (*otemplate != NULL)
        {
          obt_retain_after_finish (*otemplate);

          obj = dbt_finish_object (*otemplate);
          if (obj == NULL)
        {
          ASSERT_ERROR_AND_SET (error);

          dbt_abort_object (*otemplate);
          *otemplate = NULL;
        }
          else
        {
          bool include_new_obj;

          include_new_obj = (parser->flag.return_generated_keys && (*otemplate)->is_autoincrement_set);

          obt_quit (*otemplate);    /* free template */

          if (include_new_obj == true)
            {
              db_make_object (&db_value, obj);
              error = set_put_element (seq, obj_count, &db_value);
              if (error != NO_ERROR)
            {
              goto cleanup;
            }
              obj_count++;
            }
        }

          if (error >= NO_ERROR)
        {
          error = mq_evaluate_check_option (parser, statement->info.insert.where, obj, class_);
        }
        }

      if (parser->flag.abort)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
        }
      if (error < NO_ERROR)
        {
          break;
        }

      if (old_wait_msecs >= -1)
        {
          (void) tran_reset_wait_times (old_wait_msecs);
        }
    }
    }

  if (error < NO_ERROR)
    {
      goto cleanup;
    }

  if (*otemplate != NULL && parser->flag.return_generated_keys)
    {
      /* a client side insert with template, with requested generated keys */
      value = db_value_create ();
      if (value == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto cleanup;
    }
      error = db_make_sequence (value, seq);
      if (error != NO_ERROR)
    {
      goto cleanup;
    }
      statement->etc = (void *) value;

      goto cleanup;
    }
  else
    {
      if (!parser->flag.return_generated_keys && (*otemplate == NULL || value_clauses->next != NULL))
    {
      /* a client side insert with subquery and no template, a client side insert with multiple insert values or a
       * server side insert for which the generated keys have not been requested */
      value = db_value_create ();
      if (value == NULL)
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
          goto cleanup;
        }
      db_make_object (value, NULL);
      statement->etc = (void *) value;

      goto cleanup;
    }
    }

  if (*otemplate != NULL && value_clauses->next == NULL)
    {
      /* If any of the (nested) inserts were view objects we need to find them and create VMOPS for them.  Use a post
       * walk so that vobj will point to the vmop for the outer insert if one is needed. */
      vobj = NULL;
      statement = parser_walk_tree (parser, statement, NULL, NULL, make_vmops, &vobj);
      /* create a DB_VALUE to hold the newly inserted instance */
      ins_val = db_value_create ();
      if (ins_val == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
    }
      else
    {
      if (vobj != NULL)
        {
          /* use the virtual mop */
          db_make_object (ins_val, vobj);
        }
      else
        {
          db_make_object (ins_val, obj);
        }
      statement->etc = (void *) ins_val;
    }
    }

cleanup:
  /* free attribute descriptors */
  if (attr_descs)
    {
      for (i = 0; i < degree; i++)
    {
      if (attr_descs[i])
        {
          db_free_attribute_descriptor (attr_descs[i]);
        }
    }
      free_and_init (attr_descs);
    }

  if (update != NULL)
    {
      update->info.update.assignment = NULL;
      update->info.update.spec = NULL;
      if (update->info.update.check_where != NULL)
    {
      update->info.update.check_where->info.check_option.expr = NULL;
    }
      parser_free_tree (parser, update);
    }

  if (statement->info.insert.odku_non_null_attrs != NULL)
    {
      parser_free_tree (parser, statement->info.insert.odku_non_null_attrs);
      statement->info.insert.odku_non_null_attrs = NULL;
    }

  do_clear_insert_values (parser, statement);

  /* set NULL on labeled value */
  if (into_label != NULL && error != NO_ERROR)
    {
      DB_VALUE *db_val = pt_find_value_of_label (into_label);

      if (db_val != NULL)
    {
      db_make_object (db_val, (DB_OBJECT *) NULL);
    }
    }

  /* restore flags */
  statement->info.insert.spec->info.spec.flag = (PT_SPEC_FLAG) flag;

  if (*otemplate != NULL && error != NO_ERROR)
    {
      dbt_abort_object (*otemplate);
      *otemplate = NULL;
    }

  if (seq != NULL && error != NO_ERROR)
    {
      set_free (seq);
    }

  if (row_count_ptr != NULL)
    {
      *row_count_ptr = row_count;
    }
  return error;
}

/*
 * insert_subquery_results() - Execute sub-query & insert its results into
 *                                a target class
 *   return: Error code
 *   parser(in): Handle to the parser used to process & derive sub-query qry
 *   statement(in/out):
 *   values_list(in): The list of values to insert.
 *   class(in):
 *   savepoint_name(in):
 *
 * Note:
 *   The function requires parser is the handle to the parser used to derive qry
 *   qry is an error-free abstract syntax tree derived from a CUBRID
 *   nested SELECT, UNION, DIFFERENCE, INTERSECTION subquery.
 *   qry's select_list attributes have been expanded & type-checked.
 *   qry's select_list and attrs have the same number of elements.
 *   It modifies database state of target class and
 *   effects that executes subquery and inserts its results as new instances of
 *   target class.
 */
static int
insert_subquery_results (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE * values_list, PT_NODE * class_,
             const char **savepoint_name)
{
  int error = NO_ERROR;
  CURSOR_ID cursor_id;
  DB_OTMPL *otemplate = NULL;
  DB_OBJECT *obj = NULL;
  PT_NODE *attr, *qry, *attrs, *update = NULL;
  DB_VALUE *vals = NULL, *val = NULL;
  int degree, k, cnt, i, flag = 0;
  DB_ATTDESC **attr_descs = NULL;
  ODKU_TUPLE_VALUE_ARG odku_arg;
  int pruning_type = DB_NOT_PARTITIONED_CLASS;
  QUERY_ID query_id_self = parser->query_id;
  int obj_count = 0;
  DB_SEQ *seq = NULL;
  DB_VALUE db_value;
  DB_VALUE *value = NULL;

  assert (parser != NULL);

  if (values_list == NULL || values_list->node_type != PT_NODE_LIST
      || values_list->info.node_list.list_type != PT_IS_SUBQUERY || (qry = values_list->info.node_list.list) == NULL
      || (statement->node_type != PT_INSERT && statement->node_type != PT_MERGE))
    {
      return ER_GENERIC_ERROR;
    }
  attrs = statement->node_type == PT_MERGE ? statement->info.merge.insert.attr_list : statement->info.insert.attr_list;
  if (attrs == NULL)
    {
      return ER_GENERIC_ERROR;
    }
  error = sm_partitioned_class_type (class_->info.name.db_object, &pruning_type, NULL, NULL);
  if (error != NO_ERROR)
    {
      return error;
    }

  cnt = 0;

  if (parser->flag.return_generated_keys)
    {
      seq = set_create_sequence (0);
      if (seq == NULL)
    {
      error = ER_GENERIC_ERROR;
      return error;
    }
    }

  switch (qry->node_type)
    {
    default:            /* preconditions not met */
      return ER_GENERIC_ERROR;  /* so, nothing doing.  */

    case PT_SELECT:
    case PT_UNION:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:
      /* count elements in the select list before we modify it for ON DUPLICATE KEY UPDATE statements */
      degree = pt_length_of_select_list (pt_get_select_list (parser, qry), EXCLUDE_HIDDEN_COLUMNS);
      if (degree <= 0)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DO_INSERT_TOO_MANY, 0);
      return ER_DO_INSERT_TOO_MANY;
    }
      if (qry->etc == NULL)
    {
      if (statement->info.insert.odku_assignments != NULL)
        {
          /* Add nodes referenced in odku_assignments to the select list because we need those values when
           * evaluating values for update. */
          odku_arg.insert_stmt = statement;
          odku_arg.cursor_p = &cursor_id;

          statement->info.insert.odku_assignments =
        parser_walk_tree (parser, statement->info.insert.odku_assignments, pt_append_odku_references,
                  (void *) (&odku_arg), NULL, NULL);
          if (statement->info.insert.odku_assignments == NULL || pt_has_error (parser))
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
          if (error == NO_ERROR)
            {
              error = ER_FAILED;
            }
          return error;
        }
        }

      /* execute the subquery */
      query_id_self = parser->query_id;
      parser->query_id = NULL_QUERY_ID;
      error = do_select (parser, qry);
      if (error < NO_ERROR || qry->etc == NULL)
        {
          parser->query_id = query_id_self;
          return error;
        }
    }

      /* insert subquery results into target class */
      if (cursor_open (&cursor_id, (QFILE_LIST_ID *) qry->etc, false, false))
    {
      cursor_id.query_id = parser->query_id;

      vals = (DB_VALUE *) malloc (degree * sizeof (DB_VALUE));
      if (vals == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, degree * sizeof (DB_VALUE));
          cnt = ER_OUT_OF_VIRTUAL_MEMORY;
          goto cleanup;
        }

      for (i = 0; i < degree; i++)
        {
          db_make_null (&vals[i]);
        }

      /* allocate attribute descriptor array */
      if (degree)
        {
          attr_descs = (DB_ATTDESC **) malloc ((degree) * sizeof (DB_ATTDESC *));
          if (attr_descs == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
              degree * sizeof (DB_ATTDESC *));
          cnt = ER_OUT_OF_VIRTUAL_MEMORY;
          goto cleanup;
        }
          for (i = 0; i < degree; i++)
        {
          attr_descs[i] = NULL;
        }
        }

      /* if the list file contains more than 1 object we need to savepoint the statement to guarantee statement
       * atomicity. */
      if ((((QFILE_LIST_ID *) qry->etc)->tuple_cnt > 1 && !*savepoint_name)
          || (TM_TRAN_ISOLATION () >= TRAN_REP_READ && statement->node_type == PT_INSERT
          && statement->info.insert.odku_assignments))
        {
          *savepoint_name = mq_generate_name (parser, "UisP", &insert_savepoint_number);
          error = tran_system_savepoint (*savepoint_name);
        }

      if (error >= NO_ERROR)
        {
          /* for each tuple in subquery result do */
          while (cursor_next_tuple (&cursor_id) == DB_CURSOR_SUCCESS)
        {
          /* get current tuple of subquery result */
          if (cursor_get_tuple_value_list (&cursor_id, degree, vals) != NO_ERROR)
            {
              break;
            }

          /* create an instance of the target class using templates */
          otemplate = dbt_create_object_internal (class_->info.name.db_object);
          if (otemplate == NULL)
            {
              break;
            }
          otemplate->pruning_type = pruning_type;
          if (pruning_type != DB_NOT_PARTITIONED_CLASS)
            {
              obt_set_force_flush (otemplate);
            }
          /* update new instance with current tuple of subquery result */
          for (attr = attrs, val = vals, k = 0; attr != NULL && k < degree; attr = attr->next, val++, k++)
            {
              /* if this is the first tuple, get the attr descriptor */
              if (attr_descs != NULL)
            {
              if (attr_descs[k] == NULL)
                {
                  int is_vclass = 0;

                  /* don't get descriptors for shared attrs of views */
                  if (attr->info.name.db_object)
                {
                  is_vclass = db_is_vclass (attr->info.name.db_object);

                  if (is_vclass < 0)
                    {
                      error = is_vclass;
                    }
                }
                  if (!is_vclass)
                {
                  error =
                    db_get_attribute_descriptor (class_->info.name.db_object, attr->info.name.original,
                                 0, 1, &attr_descs[k]);
                }
                }
            }

              if (error >= NO_ERROR)
            {
              error = insert_object_attr (parser, otemplate, val, attr, attr_descs[k]);
            }

              if (error < NO_ERROR)
            {
              dbt_abort_object (otemplate);
              cursor_close (&cursor_id);
              assert (er_errid () != NO_ERROR);
              cnt = er_errid ();
              goto cleanup;
            }
            }

          if (statement->node_type == PT_INSERT && statement->info.insert.odku_assignments)
            {
              if (update == NULL)
            {
              flag = statement->info.insert.spec->info.spec.flag;
              update = do_create_odku_stmt (parser, statement);
              if (update == NULL)
                {
                  error = ER_FAILED;
                  goto cleanup;
                }
            }
              error = do_on_duplicate_key_update (parser, otemplate, update);
              if (error < 0)
            {
              /* there was an error, cleanup and return */
              cursor_close (&cursor_id);
              if (obj == NULL)
                {
                  dbt_abort_object (otemplate);
                }
              cnt = error;
              goto cleanup;
            }
              if (error > 0)
            {
              /* a successful update, go to finish */
              cnt += error;
              dbt_abort_object (otemplate);
              otemplate = NULL;
              error = NO_ERROR;
            }
            }

          if (statement->node_type == PT_INSERT && statement->info.insert.do_replace)
            {
              error =
            do_replace_into (parser, otemplate, statement->info.insert.spec,
                     statement->info.insert.class_specs);
              if (error < 0)
            {
              cursor_close (&cursor_id);
              if (obj == NULL)
                {
                  dbt_abort_object (otemplate);
                }
              cnt = error;
              goto cleanup;
            }

              cnt += error;
            }

          if (otemplate != NULL)
            {
              /* apply the object template */
              bool include_new_obj;

              obt_retain_after_finish (otemplate);

              obj = dbt_finish_object (otemplate);  /* flush template */

              include_new_obj = (obj && parser->flag.return_generated_keys && otemplate->is_autoincrement_set);

              obt_quit (otemplate); /* free template */

              if (include_new_obj == true)
            {
              db_make_object (&db_value, obj);
              error = set_put_element (seq, obj_count, &db_value);
              if (error != NO_ERROR)
                {
                  cnt = error;
                  goto cleanup;
                }
              obj_count++;
            }

              if (obj && error >= NO_ERROR)
            {
              if (statement->node_type == PT_INSERT)
                {
                  error = mq_evaluate_check_option (parser, statement->info.insert.where, obj, class_);
                }
              else if (statement->node_type == PT_MERGE && statement->info.merge.check_where)
                {
                  error =
                mq_evaluate_check_option (parser,
                              statement->info.merge.check_where->info.check_option.expr,
                              obj, class_);
                }
            }

              if (obj == NULL || error < NO_ERROR)
            {
              cursor_close (&cursor_id);
              if (obj == NULL)
                {
                  dbt_abort_object (otemplate);
                  assert (er_errid () != NO_ERROR);
                  cnt = er_errid ();
                }
              else
                {
                  cnt = error;
                }
              goto cleanup;
            }

            }

          /* keep track of how many we have inserted */
          cnt++;
        }
        }

      cursor_close (&cursor_id);
    }
    }

  if (parser->flag.return_generated_keys && seq != NULL)
    {
      value = db_value_create ();
      if (value == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto cleanup;
    }
      error = db_make_sequence (value, seq);
      if (error != NO_ERROR)
    {
      goto cleanup;
    }
      statement->etc = (void *) value;
    }

cleanup:
  if (update != NULL)
    {
      /* restore flags */
      statement->info.insert.spec->info.spec.flag = (PT_SPEC_FLAG) flag;
      update->info.update.assignment = NULL;
      update->info.update.spec = NULL;
      if (update->info.update.check_where != NULL)
    {
      update->info.update.check_where->info.check_option.expr = NULL;
    }
      parser_free_tree (parser, update);
    }
  if (vals != NULL)
    {
      for (val = vals, k = 0; k < degree; val++, k++)
    {
      db_value_clear (val);
    }
      free_and_init (vals);
    }

  if (attr_descs != NULL)
    {
      for (i = 0; i < degree; i++)
    {
      if (attr_descs[i] != NULL)
        {
          db_free_attribute_descriptor (attr_descs[i]);
        }
    }
      free_and_init (attr_descs);
    }

  if (cnt < 0 && seq != NULL)
    {
      set_free (seq);
    }

  cursor_free_self_list_id (qry->etc);
  pt_end_query (parser, query_id_self);

  return cnt;
}

/*
 * is_attr_not_in_insert_list() - Returns 1 if the name is not on the name_list,
 *              0 otherwise. name_list is assumed to be a list of PT_NAME nodes.
 *   return: Error code
 *   param1(out): Short description of the param1
 *   param2(in/out): Short description of the param2
 *   param2(in): Short description of the param3
 *
 * Note: If you feel the need
 */
static int
is_attr_not_in_insert_list (const PARSER_CONTEXT * parser, PT_NODE * name_list, const char *name)
{
  PT_NODE *tmp;
  int not_on_list = 1;

  for (tmp = name_list; tmp != NULL; tmp = tmp->next)
    {
      if (intl_identifier_casecmp (tmp->info.name.original, name) == 0)
    {
      not_on_list = 0;
      break;
    }
    }

  return not_on_list;

}               /* is_attr_not_in_insert_list */

/*
 * check_missing_non_null_attrs() - Check to see that all attributes of
 *              the class that have a NOT NULL constraint AND have no default
 *              value are present in the inserts assign list.
 *   return: Error code
 *   parser(in):
 *   spec(in):
 *   attr_list(in):
 *   has_default_values_list(in): whether this statement is used to insert
 *                                default values
 */
static int
check_missing_non_null_attrs (const PARSER_CONTEXT * parser, const PT_NODE * spec, PT_NODE * attr_list,
                  const bool has_default_values_list)
{
  DB_ATTRIBUTE *attr;
  DB_OBJECT *class_;
  int error = NO_ERROR;
  int save_au;

  if (!spec || !spec->info.spec.entity_name || !(class_ = spec->info.spec.entity_name->info.name.db_object))
    {
      return ER_GENERIC_ERROR;
    }

  AU_DISABLE (save_au);
  attr = db_get_attributes (class_);
  while (attr)
    {
      if (db_attribute_is_non_null (attr) && db_value_is_null (db_attribute_default (attr))
      && attr->default_value.default_expr.default_expr_type == DB_DEFAULT_NONE
      && (is_attr_not_in_insert_list (parser, attr_list, db_attribute_name (attr)) || has_default_values_list)
      && !(attr->flags & SM_ATTFLAG_AUTO_INCREMENT))
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_MISSING_NON_NULL_ASSIGN, 1, db_attribute_name (attr));
      error = ER_OBJ_MISSING_NON_NULL_ASSIGN;
    }
      attr = db_attribute_next (attr);
    }
  AU_ENABLE (save_au);

  return error;
}

/*
 * make_vmops() -
 *   return: Error code
 *   parser(in): Short description of the param1
 *   node(in):
 *   arg(in/out):
 *   continue_walk(in/out):
 */
static PT_NODE *
make_vmops (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  DB_OBJECT **vobj = ((DB_OBJECT **) arg);
  DB_OBJECT *vclass_mop, *obj;
  const char *into_label;
  DB_VALUE *val;

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

  /* make a virtual obj if it is a virtual class and has an into label */
  if (node->info.insert.into_var
      && ((vclass_mop = node->info.insert.spec->info.spec.flat_entity_list->info.name.virt_object) != NULL))
    {
      into_label = node->info.insert.into_var->info.name.original;
      val = pt_find_value_of_label (into_label);
      if (val != NULL)
    {
      obj = db_get_object (val);
      *vobj = vid_build_virtual_mop (obj, vclass_mop);
      /* change the label to point to the newly created vmop, we don't need to call pt_associate_label_with_value
       * here because we've directly modified the value that has been installed in the table. */
      db_make_object (val, *vobj);
    }
    }
  else
    {
      *vobj = NULL;
    }

  return node;

}

/*
 * test_check_option() - Tests if we are inserting to a class through a view
 *                         with a check option.
 *   return: Error code
 *   parser(in): Parser context
 *   node(in): The PT_NAME node of a potential insert
 *   arg(in/out): Nonzero iff insert statement has a check option
 *   continue_walk(in/out):
 */
static PT_NODE *
test_check_option (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  int *found = (int *) arg;
  PT_NODE *class_;
  DB_OBJECT *view;

  if (node->node_type != PT_INSERT || !node->info.insert.spec)
    {
      return node;
    }

  /* make a virtual obj if it is a virtual class and has an into label */
  class_ = node->info.insert.spec->info.spec.flat_entity_list;
  view = class_->info.name.virt_object;
  if (view)
    {
      if (sm_get_class_flag (view, SM_CLASSFLAG_WITHCHECKOPTION) > 0
      || sm_get_class_flag (view, SM_CLASSFLAG_LOCALCHECKOPTION) > 0)
    {
      *found = 1;
      *continue_walk = PT_STOP_WALK;
    }
    }

  return node;

}

/*
 * insert_local () - Insert execution checks:
 *           1. Updates attribute default expressions.
 *           2. Checks for missing attributes that have not null
 *          constraints.
 *           3. Check if inserting a false sub-query.
 *           4. Check if a savepoint is needed.
 *           5. Calls do_insert_template.
 *           6. Revert to savepoint is an error occurred.
 *
 * return     : Error code or row count if no error has occurred.
 * parser (in)    : Parser context.
 * statement (in) : Parse tree node for insert statement.
 */
static int
insert_local (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  int row_count_total = 0;
  PT_NODE *class_ = NULL, *vc = NULL;
  int save;
  int has_check_option = 0;
  const char *savepoint_name = NULL;
  PT_NODE *crt_list = NULL;
  bool has_default_values_list = false;
  bool is_multiple_tuples_insert = false;
  bool need_savepoint = false;
  int has_trigger = 0;
  bool is_trigger_involved = false;
  DB_OTMPL *otemplate = NULL;

  if (!statement || statement->node_type != PT_INSERT || !statement->info.insert.spec
      || !statement->info.insert.spec->info.spec.flat_entity_list)
    {
      return ER_GENERIC_ERROR;
    }

  class_ = statement->info.insert.spec->info.spec.flat_entity_list;

  statement->etc = NULL;

  for (crt_list = statement->info.insert.value_clauses, has_default_values_list = false; crt_list != NULL;
       crt_list = crt_list->next)
    {
      if (crt_list->info.node_list.list_type == PT_IS_DEFAULT_VALUE)
    {
      has_default_values_list = true;
      break;
    }
    }

  error = do_evaluate_default_expr (parser, class_);
  if (error != NO_ERROR)
    {
      return error;
    }

  error =
    check_missing_non_null_attrs (parser, statement->info.insert.spec, statement->info.insert.attr_list,
                  has_default_values_list);
  if (error != NO_ERROR)
    {
      return error;
    }

  crt_list = statement->info.insert.value_clauses;
  if (crt_list->next != NULL)
    {
      is_multiple_tuples_insert = true;
    }

  if (crt_list->info.node_list.list_type == PT_IS_SUBQUERY && (vc = crt_list->info.node_list.list)
      && pt_false_where (parser, vc))
    {
      /* 0 tuples inserted. */
      return 0;
    }

  /*
   * It is necessary to add savepoint in the cases as below.
   *
   * 1. when multiple tuples were inserted (ex: insert into ... values(), (), ();)
   * 2. the REPLACE statement (ex: replace into ... values ..;)
   * 3. view having 'with check option'
   * 4. class/view having trigger
   * 5. when there is another insert statement among values.
   */

  if (is_multiple_tuples_insert == true || statement->info.insert.do_replace == true)
    {
      need_savepoint = true;
    }

  if (need_savepoint == false)
    {
      statement = parser_walk_tree (parser, statement, NULL, NULL, test_check_option, &has_check_option);
      if (has_check_option)
    {
      need_savepoint = true;
    }
    }

  /* DO NOT RETURN UNTIL AFTER AU_ENABLE! */
  AU_DISABLE (save);
  parser->au_save = save;

  if (need_savepoint == false && statement->info.insert.odku_assignments != NULL)
    {
      has_trigger = 0;
      error = sm_class_has_triggers (class_->info.name.db_object, &has_trigger, TR_EVENT_UPDATE);
      if (error != NO_ERROR)
    {
      AU_ENABLE (save);
      return error;
    }
      if (has_trigger != 0)
    {
      need_savepoint = true;
    }
      else if (TM_TRAN_ISOLATION () >= TRAN_REP_READ && !statement->info.insert.server_allowed)
    {
      need_savepoint = true;
    }
    }

  if (need_savepoint == false)
    {
      has_trigger = 0;
      error = sm_class_has_triggers (class_->info.name.db_object, &has_trigger, TR_EVENT_INSERT);
      if (error != NO_ERROR)
    {
      AU_ENABLE (save);
      return error;
    }
      if (has_trigger != 0)
    {
      need_savepoint = true;
    }
    }

  if (need_savepoint == false)
    {
      int arg[2];       /* argument for pt_find_node_type_pre */
      arg[0] = PT_INSERT;   /* node type */
      arg[1] = 0;       /* found */
      (void) parser_walk_tree (parser, statement->info.insert.value_clauses, pt_find_node_type_pre, arg, NULL, NULL);
      if (arg[1] == 1)
    {
      /* sub insert was found */
      need_savepoint = true;
    }
    }

  /*
   *  if the insert statement contains more than one insert component,
   *  we savepoint the insert components to try to guarantee insert
   *  statement atomicity.
   */
  if (need_savepoint == true)
    {
      savepoint_name = mq_generate_name (parser, "UisP", &insert_savepoint_number);
      if (savepoint_name == NULL)
    {
      AU_ENABLE (save);
      return ER_GENERIC_ERROR;
    }
      error = tran_system_savepoint (savepoint_name);
      if (error != NO_ERROR)
    {
      AU_ENABLE (save);
      return error;
    }
    }

  /* the do_Trigger_involved will be set as true when execute trigger statement. it will not be set back. we need to
   * keep its value to update last insert id. */
  is_trigger_involved = do_Trigger_involved;

  cdc_Trigger_involved = do_Trigger_involved;

  if (!do_Trigger_involved)
    {
      obt_begin_insert_values ();
    }

  row_count_total = 0;

  error = do_insert_template (parser, &otemplate, statement, &savepoint_name, &row_count_total);

  AU_ENABLE (save);

  /* restore the obt_Last_insert_id_generated flag after insert. */
  if (!is_trigger_involved && obt_Last_insert_id_generated)
    {
      obt_Last_insert_id_generated = false;
      if (error != NO_ERROR)
    {
      (void) csession_reset_cur_insert_id ();
    }
    }

  /* if error and a savepoint was created, rollback to savepoint. No need to rollback if the TM aborted the
   * transaction. */
  if (error < NO_ERROR && savepoint_name && (error != ER_LK_UNILATERALLY_ABORTED))
    {
      /* savepoint from tran_savepoint() */
      (void) tran_internal_abort_upto_savepoint (savepoint_name, SYSTEM_SAVEPOINT, true);
      /* Use a special version of rollback which will not clobber cached views. We can do this because we know insert
       * can not have created any views. This is instead of the extern function: db_abort_to_savepoint(savepoint_name); */
    }

  return error < 0 ? error : row_count_total;
}

/*
 * do_insert() - Checks if insert is already prepared and execute it. If it
 *       is not prepared, calls insert_local.
 *
 * return   : Error code if insert fails, the row count otherwise.
 * parser (in)  : Parser context.
 * statement(in): Parse tree of a insert statement.
 */
int
do_insert (PARSER_CONTEXT * parser, PT_NODE * root_statement)
{
  PT_NODE *statement = root_statement;
  int error;

  CHECK_MODIFICATION_ERROR ();

  if (statement->info.insert.spec->info.spec.remote_server_name)
    {
      error = do_insert_at_server (parser, statement);
    }
  else
    {
      error = insert_local (parser, statement);
    }

  if (pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_EXECUTION);
    }

  while (error < NO_ERROR && statement->next)
    {
      /* assume error was from mismatch of multiple possible translated inserts. Try the next statement in the list.
       * Only report the last error. */
      parser_free_tree (parser, parser->error_msgs);
      parser->error_msgs = NULL;

      statement = statement->next;
      error = insert_local (parser, statement);

      /* check whether this transaction is a victim of deadlock during */
      /* request to the driver */
      if (parser->flag.abort)
    {
      assert (er_errid () != NO_ERROR);
      return (er_errid ());
    }

      if (pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_EXECUTION);
    }

      /* This is to allow the row "counting" to be done in db_execute_and_keep_statement, and also correctly returns
       * the "result" of the last insert statement. Only the first insert statement in the list is examined for
       * results. */
      root_statement->etc = statement->etc;
      statement->etc = NULL;
    }

  return error;
}

/*
 * do_prepare_insert () - Prepare the INSERT statement
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in):
 */
int
do_prepare_insert (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  PT_NODE *class_;
  PT_NODE *values = NULL;
  PT_NODE *attr_list, *value_clauses, *query;
  PT_NODE *update = NULL;
  PT_NODE *with = NULL;
  int save_au;

  if (statement == NULL || statement->node_type != PT_INSERT || statement->info.insert.spec == NULL)
    {
      assert (false);
      return ER_GENERIC_ERROR;
    }
  if (statement->info.insert.spec->info.spec.flat_entity_list == NULL)
    {
      if (statement->info.insert.spec->info.spec.remote_server_name == NULL)
    {
      assert (false);
      return ER_GENERIC_ERROR;
    }
    }

  AU_DISABLE (save_au);

  /* We do not allow multi statements. To be checked! */
  if (pt_length_of_list (statement) > 1)
    {
      assert (false);
      goto cleanup;
    }

  /* there can be no results, this is a compile time false where clause */
  value_clauses = statement->info.insert.value_clauses;
  if (value_clauses && value_clauses->info.node_list.list_type == PT_IS_SUBQUERY)
    {
      query = value_clauses->info.node_list.list;
      if (PT_IS_SELECT (query) && pt_false_where (parser, query))
    {
      /* tell to the execute routine that there's no XASL to execute */
      statement->xasl_id = NULL;
      goto cleanup;
    }
    }

  statement->etc = NULL;
  if (statement->info.insert.spec->info.spec.flat_entity_list)
    {
      class_ = statement->info.insert.spec->info.spec.flat_entity_list;
      values = statement->info.insert.value_clauses;

      error = do_insert_checks (parser, statement, &class_, &update, values);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto cleanup;
    }

      if (statement->info.insert.server_allowed != SERVER_INSERT_IS_ALLOWED)
    {
      goto cleanup;
    }
    }

  error = do_prepare_insert_internal (parser, statement);

cleanup:
  /* Free update attribute. */
  if (update != NULL)
    {
      update->info.update.assignment = NULL;
      update->info.update.spec = NULL;
      if (update->info.update.check_where != NULL)
    {
      update->info.update.check_where->info.check_option.expr = NULL;
    }
      parser_free_tree (parser, update);
    }

  AU_ENABLE (save_au);

  return error;
}

/*
 * do_execute_insert () - Execute the prepared INSERT statement
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in):
 */
int
do_execute_insert (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  INT64 err;
  QFILE_LIST_ID *list_id;
  QUERY_FLAG query_flag;
  QUERY_ID query_id_self = parser->query_id;

  assert (parser->query_id == NULL_QUERY_ID);

  CHECK_MODIFICATION_ERROR ();

  /* for dblink: no need to check xasl_id is NULL */
  if (statement->info.insert.spec->info.spec.remote_server_name == NULL)
    {
      if (statement->xasl_id == NULL)
    {
      /* check if it is not necessary to execute this statement */
      if (qo_need_skip_execution ())
        {
          statement->etc = NULL;
          return NO_ERROR;
        }
      return do_insert (parser, statement);
    }
    }

  query_flag = DEFAULT_EXEC_MODE;

  query_flag |= NOT_FROM_RESULT_CACHE;
  query_flag |= RESULT_CACHE_INHIBITED;

  if (parser->flag.return_generated_keys)
    {
      query_flag |= RETURN_GENERATED_KEYS;
    }

  if (parser->flag.is_xasl_pinned_reference)
    {
      query_flag |= XASL_CACHE_PINNED_REFERENCE;
    }

  if (statement->flag.use_auto_commit)
    {
      query_flag |= EXECUTE_QUERY_WITH_COMMIT;
    }

  if (parser->flag.is_auto_commit)
    {
      query_flag |= TRAN_AUTO_COMMIT;
    }

  if (prm_get_bool_value (PRM_ID_QUERY_TRACE) == true && parser->query_trace == true)
    {
      do_set_trace_to_query_flag (&query_flag);
      do_send_plan_trace_to_session (parser);
    }

  if (ws_need_flush ())
    {
      if (statement->flag.use_auto_commit)
    {
      // When a transaction is under auto-commit mode, flush all dirty objects to server.
      err = tran_flush_to_commit ();
      if (err != NO_ERROR)
        {
          return err;
        }
      /* Nothing to flush. However ws_Num_dirty_mop is not 0 sometimes. We may reset ws_Num_dirty_mop to 0,
       * if flushed without errors, but is not necessary. Before sending data to the server, we check that
       * the transaction was not finalized, in case of execution with commit.
       */
    }
    }

  assert (parser->query_id == NULL_QUERY_ID);
  list_id = NULL;

  err = execute_query (statement->xasl_id, &parser->query_id, parser->host_var_count + parser->auto_param_count,
               parser->host_variables, &list_id, query_flag, NULL, NULL);

  /* free returned QFILE_LIST_ID */
  if (list_id)
    {
      /* set as result */
      err = list_id->tuple_cnt;
      if (parser->flag.return_generated_keys)
    {
      statement->etc = (void *) list_id;
    }
      else
    {
      cursor_free_self_list_id (list_id);
    }
    }

  /* end the query; reset query_id and call qmgr_end_query() */
  pt_end_query (parser, query_id_self);

  return err;
}

/*
 * Function Group:
 * Implement method calls
 *
 */

static int call_method (PARSER_CONTEXT * parser, PT_NODE * statement);

/*
 * call_method() -
 *   return: Value returned by method if success, otherwise an error code
 *   parser(in): Parser context
 *   node(in): Parse tree of a call statement
 *
 * Note:
 */
static int
call_method (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  const char *into_label, *proc;
  int error = NO_ERROR;
  DB_OBJECT *obj = NULL;
  DB_VALUE target_value, *ins_val, ret_val, db_value;
  DB_VALUE_LIST *val_list = 0, *vl, **next_val_list;
  PT_NODE *vc, *into, *target, *method;

  db_make_null (&ret_val);
  db_make_null (&target_value);

  /*
   * The method name and ON name are required.
   */
  if (!statement || !(method = statement->info.method_call.method_name) || method->node_type != PT_NAME
      || !(proc = method->info.name.original) || !(target = statement->info.method_call.on_call_target))
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      return er_errid ();
    }

  /*
   * Determine whether the object is a class or instance.
   */

  pt_evaluate_tree (parser, target, &target_value, 1);
  if (pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_SEMANTIC);
      return er_errid ();
    }

  if (DB_VALUE_TYPE (&target_value) == DB_TYPE_NULL)
    {
      /*
       * Don't understand the rationale behind this case.  What's the
       * point here?  MRS 4/30/96
       */
      error = NO_ERROR;
    }
  else
    {
      if (DB_VALUE_TYPE (&target_value) == DB_TYPE_OBJECT)
    {
      obj = db_get_object ((&target_value));
    }

      if (obj == NULL || pt_has_error (parser))
    {
      PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_METH_TARGET_NOT_OBJ);
      return er_errid ();
    }

      /*
       * Build an argument list.
       */
      next_val_list = &val_list;
      vc = statement->info.method_call.arg_list;
      for (; vc != NULL; vc = vc->next)
    {
      DB_VALUE *db_val;
      bool to_break = false;

      *next_val_list = (DB_VALUE_LIST *) calloc (1, sizeof (DB_VALUE_LIST));
      if (*next_val_list == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (DB_VALUE_LIST));
          return er_errid ();
        }
      (*next_val_list)->next = (DB_VALUE_LIST *) 0;

      /*
       * Don't clone host vars; they may actually be acting as output
       * variables (e.g., a character array that is intended to receive
       * bytes from the method), and cloning will ensure that the
       * results never make it to the expected area.  Since
       * pt_evaluate_tree() always clones its db_values we must not
       * use pt_evaluate_tree() to extract the db_value from a host
       * variable;  instead extract it ourselves.
       */
      if (PT_IS_CONST (vc))
        {
          db_val = pt_value_to_db (parser, vc);
        }
      else
        {
          pt_evaluate_tree (parser, vc, &db_value, 1);
          if (pt_has_error (parser))
        {
          /* to maintain the list to free all the allocated */
          to_break = true;
        }
          db_val = &db_value;
        }

      if (db_val != NULL)
        {
          (*next_val_list)->val = *db_val;

          next_val_list = &(*next_val_list)->next;
        }

      if (to_break)
        {
          break;
        }
    }

      /*
       * Call the method.
       */
      if (pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_SEMANTIC);
      error = er_errid ();
    }
      else
    {
      error = db_send_arglist (obj, proc, &ret_val, val_list);
    }

      /*
       * Free the argument list.  Again, it is important to be careful
       * with host variables.  Since we didn't clone them, we shouldn't
       * free or clear them.
       */
      vc = statement->info.method_call.arg_list;
      for (; val_list && vc; vc = vc->next)
    {
      vl = val_list->next;
      if (!PT_IS_CONST (vc))
        {
          db_value_clear (&val_list->val);
        }
      free_and_init (val_list);
      val_list = vl;
    }

      if (error == NO_ERROR)
    {
      /*
       * Save the method result.
       */
      statement->etc = (void *) db_value_copy (&ret_val);

      if ((into = statement->info.method_call.to_return_var) != NULL && into->node_type == PT_NAME
          && (into_label = into->info.name.original) != NULL)
        {
          /* create another DB_VALUE of the new instance for the label_table */
          ins_val = db_value_copy (&ret_val);

          /* enter {label, ins_val} pair into the label_table */
          error = pt_associate_label_with_value_check_reference (into_label, ins_val);
        }
    }
    }

  db_value_clear (&ret_val);
  return error;
}

/*
 * do_call_method() -
 *   return: Value returned by method if success, otherwise an error code
 *   parser(in): Parser context
 *   node(in): Parse tree of a call statement
 *
 * Note:
 */
int
do_call_method (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  PT_NODE *method;

  if (!statement || !(method = statement->info.method_call.method_name) || method->node_type != PT_NAME
      || !(method->info.name.original))
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      return er_errid ();
    }

  if (statement->info.method_call.on_call_target)
    {
      return call_method (parser, statement);
    }
  else
    {
      return jsp_call_stored_procedure (parser, statement);
    }
}

/*
 * Function Group:
 * Functions for the implementation of virtual queries.
 *
 */

/*
 * do_select() -
 *   return: Error code
 *   parser(in/out): Parser context
 *   statement(in/out): A statement to do
 *
 * Note: Side effects can exist at returned result through application extern
 */

int
do_select (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  return do_select_internal (parser, statement, false);
}

/*
 * do_select_for_ins_upd() -
 *   return: Error code
 *   parser(in/out): Parser context
 *   statement(in/out): A statement to do
 *
 * Note: Side effects can exist at returned result through application extern
 */
int
do_select_for_ins_upd (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  return do_select_internal (parser, statement, true);
}

/*
 * do_select_internal() - do_insert internal routine
 *   return: Error code
 *   parser(in/out): Parser context
 *   statement(in/out): A statement to do
 *   for_inst_upd: check insert/update statement
 *
 * Note: Side effects can exist at returned result through application extern
 */
static int
do_select_internal (PARSER_CONTEXT * parser, PT_NODE * statement, bool for_ins_upd)
{
  int error;
  XASL_NODE *xasl = NULL;
  QFILE_LIST_ID *list_id = NULL;
  int into_cnt, i;
  PT_NODE *into;
  const char *into_label;
  DB_VALUE *vals, *v;
  int save;
  QUERY_FLAG query_flag;
  XASL_STREAM stream;
  bool query_trace = false;

  assert (parser->query_id == NULL_QUERY_ID);

  init_xasl_stream (&stream);

  error = NO_ERROR;

  /* click counter check */
  if (statement->flag.is_click_counter)
    {
      CHECK_MODIFICATION_ERROR ();
    }

  AU_DISABLE (save);
  parser->au_save = save;

  /* mark the beginning of another level of xasl packing */
  pt_enter_packing_buf ();

  if (prm_get_bool_value (PRM_ID_QUERY_TRACE) == true && parser->query_trace == true)
    {
      query_trace = true;
    }

  query_flag = DEFAULT_EXEC_MODE;

  if (parser->flag.dont_collect_exec_stats)
    {
      query_flag |= DONT_COLLECT_EXEC_STATS;
    }

  if (parser->flag.is_auto_commit)
    {
      query_flag |= TRAN_AUTO_COMMIT;
    }

#if defined(CUBRID_DEBUG)
  PT_NODE_PRINT_TO_ALIAS (parser, statement, PT_CONVERT_RANGE);
#endif

  pt_null_etc (statement);

  xasl = parser_generate_xasl (parser, statement);

  if (xasl && !pt_has_error (parser))
    {
      if (for_ins_upd)
    {
      if (xasl->outptr_list)
        {
          for (REGU_VARIABLE_LIST regu_var_list = xasl->outptr_list->valptrp; regu_var_list;
           regu_var_list = regu_var_list->next)
        {
          regu_var_list->value.flags |= REGU_VARIABLE_UPD_INS_LIST;
        }
        }
    }

      if (pt_false_where (parser, statement))
    {
      /* there is no results, this is a compile time false where clause */
    }
      else
    {
      if (query_trace == true)
        {
          do_set_trace_to_query_flag (&query_flag);
          do_send_plan_trace_to_session (parser);
        }

      if (error >= NO_ERROR)
        {
          error = xts_map_xasl_to_stream (xasl, &stream);
          if (error != NO_ERROR)
        {
          PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
        }
        }

      if (error >= NO_ERROR)
        {
          error =
        prepare_and_execute_query (stream.buffer, stream.buffer_size, &parser->query_id,
                       parser->host_var_count + parser->auto_param_count, parser->host_variables,
                       &list_id, query_flag);
        }
      statement->etc = list_id;

      /* free 'stream' that is allocated inside of xts_map_xasl_to_stream() */
      if (stream.buffer)
        {
          free_and_init (stream.buffer);
        }

      if (error >= NO_ERROR)
        {
          /* if select ... into label ... has some result val then enter {label,val} pair into the label_table */
          into = statement->info.query.into_list;

          into_cnt = pt_length_of_list (into);
          if (into_cnt > 0 && (vals = (DB_VALUE *) malloc (into_cnt * sizeof (DB_VALUE))) != NULL)
        {
          if (pt_get_one_tuple_from_list_id (parser, statement, vals, into_cnt))
            {
              for (i = 0, v = vals; i < into_cnt && into; i++, v++, into = into->next)
            {
              if (into->node_type == PT_NAME && (into_label = into->info.name.original) != NULL)
                {
                  error = pt_associate_label_with_value_check_reference (into_label, db_value_copy (v));
                }
              db_value_clear (v);
            }
            }
          else if (into->node_type == PT_NAME)
            {
              PT_ERRORmf (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_PARM_IS_NOT_SET,
                  into->info.name.original);
            }
          free_and_init (vals);
        }
        }
      else
        {
          assert (er_errid () != NO_ERROR);
          error = er_errid ();
          if (error == NO_ERROR)
        {
          error = ER_REGU_SYSTEM;
        }
        }
    }           /* else */
    }
  else
    {
      error = er_errid ();
      if (error == NO_ERROR && pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_SEMANTIC);
      pt_reset_error (parser);

      error = er_errid ();
    }

      assert (er_errid () != NO_ERROR);
      if (error == NO_ERROR)
    {
      error = ER_FAILED;
    }
    }

  /* mark the end of another level of xasl packing */
  pt_exit_packing_buf ();

  AU_ENABLE (save);
  return error;
}

static PT_NODE *
pt_sub_host_vars_index (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  PT_NODE **host_var_p = (PT_NODE **) arg;


  if (node->node_type == PT_NAME && node->info.name.constant_value)
    {
      PT_NODE *hv = node->info.name.constant_value;

      /* the host var. by lambda node should be excluded from subquery's host variable */
      if (hv->node_type == PT_HOST_VAR)
    {
      hv->info.host_var.saved = hv->info.host_var.index + 1;
    }
    }

  /* the host var. having same index should be linked each other */
  if (node->node_type == PT_HOST_VAR && node->info.host_var.index >= 0)
    {
      node->info.host_var.next = host_var_p[node->info.host_var.index];
      host_var_p[node->info.host_var.index] = node;
    }

  *continue_walk = PT_CONTINUE_WALK;

  return node;
}

/*
 * do_prepare_select() - Prepare the SELECT statement including optimization and
 *                       plan generation, and creating XASL as the result
 *   return: Error code
 *   parser(in/out): Parser context
 *   statement(in/out): A statement to do
 *
 * Note:
 */
int
do_prepare_select (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int err = NO_ERROR;
  int au_save;

  COMPILE_CONTEXT *contextp;
  XASL_STREAM stream;

  contextp = &parser->context;

  init_xasl_stream (&stream);

  if (parser == NULL || statement == NULL)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  contextp->sql_user_text = statement->sql_user_text;
  contextp->sql_user_text_len = statement->sql_user_text_len;

  /* click counter check */
  if (statement->flag.is_click_counter)
    {
      CHECK_MODIFICATION_ERROR ();
    }

  /* there can be no results, this is a compile time false where clause */
  if (pt_false_where (parser, statement))
    {
      /* tell to the execute routine that there's no XASL to execute */
      statement->xasl_id = NULL;
      return NO_ERROR;
    }

  /* if already prepared */
  if (statement->xasl_id)
    {
      return NO_ERROR;
    }

  /* make query string */
  parser->flag.dont_prt_long_string = 1;
  parser->flag.long_string_skipped = 0;
  parser->flag.print_type_ambiguity = 0;

  PT_NODE_PRINT_TO_ALIAS (parser, statement,
              (CUSTOM_PRINT_4_SHA_COMPUTE | PT_PRINT_DIFFERENT_SYSTEM_PARAMETERS | PT_PRINT_LOWER));

  contextp->sql_hash_text = (char *) statement->alias_print;
  err =
    SHA1Compute ((unsigned char *) contextp->sql_hash_text, (unsigned) strlen (contextp->sql_hash_text),
         &contextp->sha1);
  if (err != NO_ERROR)
    {
      ASSERT_ERROR ();
      return err;
    }
  parser->flag.dont_prt_long_string = 0;
  if (parser->flag.long_string_skipped || parser->flag.print_type_ambiguity)
    {
      statement->flag.cannot_prepare = 1;
      return NO_ERROR;
    }

  /* look up server's XASL cache for this query string and get XASL file id (XASL_ID) returned if found */
  contextp->recompile_xasl = statement->flag.recompile;
  if (statement->flag.recompile == 0)
    {
      XASL_NODE_HEADER xasl_header;
      stream.xasl_header = &xasl_header;

      err = prepare_query (contextp, &stream);
      if (err != NO_ERROR)
    {
      ASSERT_ERROR_AND_SET (err);
    }
      else if (contextp->recompile_xasl == true)
    {
      /* recompile flag was returned by server */
      if (stream.xasl_id != NULL)
        {
          free_and_init (stream.xasl_id);
        }
    }
      else if (stream.xasl_id != NULL)
    {
      if (pt_recompile_for_like_optimizations (parser, statement, stream.xasl_header->xasl_flag))
        {
          contextp->recompile_xasl = true;
          if (stream.xasl_id != NULL)
        {
          free_and_init (stream.xasl_id);
        }
        }
      /* check xasl header */
      /* TODO: we can treat the different cases of MRO by hacking query string. */
      if (pt_recompile_for_limit_optimizations (parser, statement, stream.xasl_header->xasl_flag))
        {
          contextp->recompile_xasl = true;
          if (stream.xasl_id != NULL)
        {
          free_and_init (stream.xasl_id);
        }
        }
    }
    }

  if (stream.xasl_id == NULL && err == NO_ERROR)
    {
      /* cache not found; make XASL from the parse tree including query optimization and plan generation */

      /* mark the beginning of another level of xasl packing */
      pt_enter_packing_buf ();

      AU_SAVE_AND_DISABLE (au_save);    /* this prevents authorization checking during generating XASL */
      /* parser_generate_xasl() will build XASL tree from parse tree */
      contextp->xasl = parser_generate_xasl (parser, statement);
      if (contextp->xasl && statement->info.query.oids_included)
    {
      contextp->xasl->header.xasl_flag |= RESULT_CACHE_INHIBITED;
    }
      AU_RESTORE (au_save);

      if (contextp->xasl && (err == NO_ERROR) && !pt_has_error (parser))
    {
      /* convert the created XASL tree to the byte stream for transmission to the server */
      err = xts_map_xasl_to_stream (contextp->xasl, &stream);
      if (err != NO_ERROR)
        {
          PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
        }
    }
      else
    {
      err = er_errid ();
      if (err == NO_ERROR && pt_has_error (parser))
        {
          pt_report_to_ersys (parser, PT_SEMANTIC);
          pt_reset_error (parser);

          err = er_errid ();
        }

      assert (er_errid () != NO_ERROR);
      if (err == NO_ERROR)
        {
          err = ER_FAILED;
        }
    }

      /* request the server to prepare the query; give XASL stream generated from the parse tree and get XASL file id
       * returned */
      if (stream.buffer && (err == NO_ERROR))
    {
      err = prepare_query (contextp, &stream);
      if (err != NO_ERROR)
        {
          assert (er_errid () != NO_ERROR);
          err = er_errid ();
        }
    }

      /* mark the end of another level of xasl packing */
      pt_exit_packing_buf ();

      /* As a result of query preparation of the server, the XASL cache for this query will be created or updated. */

      /* free 'stream' that is allocated inside of xts_map_xasl_to_stream() */
      if (stream.buffer)
    {
      free_and_init (stream.buffer);
    }
      statement->flag.use_plan_cache = 0;
    }
  else
    {
      if (err == NO_ERROR)
    {
      statement->flag.use_plan_cache = 1;
    }
      else
    {
      statement->flag.use_plan_cache = 0;
    }
    }

  /* save the XASL_ID that is allocated and returned by prepare_query() into 'statement->xasl_id' to be used by
   * do_execute_select() */
  statement->xasl_id = stream.xasl_id;

  return err;
}               /* do_prepare_select() */

/*
 * do_prepare_session_statement () - prepare step for a prepared session
 *                   statement
 * return : error code or NO_ERROR
 * parser (in)    : parser context
 * statement (in) : prepared statement
 */
int
do_prepare_session_statement (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  assert (statement->node_type == PT_EXECUTE_PREPARE);
  if (statement->xasl_id != NULL)
    {
      /* already "prepared" */
      return NO_ERROR;
    }
  statement->xasl_id = (XASL_ID *) malloc (sizeof (XASL_ID));
  if (statement->xasl_id == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (XASL_ID));
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }
  XASL_ID_COPY (statement->xasl_id, &statement->info.execute.xasl_id);
  return NO_ERROR;
}

/*
 * do_prepare_subquery () - prepare subquery statements for query cache
 * return : Error code
 * parser (in)    : parser context
 * stmt (in) : subquery statement to prepare
 */
int
do_prepare_subquery (PARSER_CONTEXT * parser, PT_NODE * stmt)
{
  int err = NO_ERROR;
  PARSER_CONTEXT context;
  int i, sub_idx, var_count;
  PT_NODE *hv, *save_next = NULL;
  PT_NODE **host_var_p, *prev;
  PT_MISC_TYPE save_flag;

  context = *parser;

  var_count = parser->host_var_count + parser->auto_param_count;

  stmt->info.query.flag.subquery_cached = 1;

  context.host_var_count = 0;
  context.host_variables = NULL;
  stmt->sub_host_var_index = NULL;
  stmt->sub_host_var_count = 0;

  if (var_count > 0)
    {
      host_var_p = (PT_NODE **) calloc (var_count, sizeof (PT_NODE *));
      if (host_var_p == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, var_count * sizeof (PT_NODE *));
      goto err_exit;
    }

      context.host_variables = (DB_VALUE *) malloc (var_count * sizeof (DB_VALUE));
      if (context.host_variables == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, var_count * sizeof (DB_VALUE));
      goto err_exit;
    }

      stmt->sub_host_var_index = (int *) parser_alloc (parser, var_count * sizeof (int));
      if (stmt->sub_host_var_index == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, var_count * sizeof (int));
      goto err_exit;
    }

      /* not to traverse next subquery */
      save_next = stmt->next;
      stmt->next = NULL;

      parser_walk_tree (&context, stmt, pt_sub_host_vars_index, host_var_p, NULL, NULL);

      /* restore next subquery */
      stmt->next = save_next;
    }

  for (i = 0; i < var_count; i++)
    {
      if (host_var_p[i])
    {
      for (hv = host_var_p[i]; hv; hv = hv->info.host_var.next)
        {
          /* saved flag indicates whether it can be used as a host variable. */
          if (!hv->info.host_var.saved)
        {
          sub_idx = stmt->sub_host_var_count;
          stmt->sub_host_var_index[sub_idx] = i;
          db_value_clone (&parser->host_variables[i], &context.host_variables[sub_idx]);
          stmt->sub_host_var_count++;
          break;
        }
        }

      for (; hv; hv = hv->info.host_var.next)
        {
          if (!hv->info.host_var.saved)
        {
          /* set the saved flag for host variables */
          hv->info.host_var.saved = hv->info.host_var.index + 1;
          hv->info.host_var.index = sub_idx;
        }
        }
    }
    }

  context.host_var_count = stmt->sub_host_var_count;

  /* save the flag for main query's prepare */
  save_flag = stmt->info.query.is_subquery;

  err = do_prepare_select (&context, stmt);

  /* restore the flag */
  stmt->info.query.is_subquery = save_flag;

  /* restore host var index */
  for (i = 0; i < var_count; i++)
    {
      prev = NULL;
      for (hv = host_var_p[i]; hv; hv = hv->info.host_var.next)
    {
      if (prev)
        {
          prev->info.host_var.next = NULL;
        }
      hv->info.host_var.index = hv->info.host_var.saved - 1;
      hv->info.host_var.saved = 0;
      prev = hv;
    }
    }

  if (var_count > 0)
    {
      free (host_var_p);

      /* clear for only cloned */
      for (i = 0; i < stmt->sub_host_var_count; i++)
    {
      db_value_clear (&context.host_variables[i]);
    }
      free (context.host_variables);
    }

  return err;

err_exit:

  if (host_var_p)
    {
      free (host_var_p);
    }

  if (context.host_variables)
    {
      free (context.host_variables);
    }

  return ER_OUT_OF_VIRTUAL_MEMORY;
}

/*
 * do_execute_prepared_subquery () - execute CTE statements as prepared statement for result-cache
 * return : Error code
 * parser (in)   : parser context
 * num_query : the number of subqueries
 * info (in) : prepare info. for subquery 
 */
int
do_execute_prepared_subquery (PARSER_CONTEXT * parser, PT_NODE * stmt, int num_query, DB_PREPARE_SUBQUERY_INFO * info)
{
  int i, q, err = NO_ERROR;
  QUERY_ID query_id;
  QFILE_LIST_ID *list_id;

  for (q = 0; q < num_query; q++)
    {
      DB_VALUE *host_variables = NULL;

      if (info[q].host_var_count > 0)
    {
      host_variables = (DB_VALUE *) malloc (sizeof (DB_VALUE) * info[q].host_var_count);

      if (host_variables == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
              sizeof (DB_VALUE) * info[q].host_var_count);
          err = ER_OUT_OF_VIRTUAL_MEMORY;
          return err;
        }

      for (i = 0; i < info[q].host_var_count; i++)
        {
          pr_clone_value (&parser->host_variables[info[q].host_var_index[i]], &host_variables[i]);
        }
    }

      err =
    execute_query (&info[q].xasl_id, &query_id, info[q].host_var_count, host_variables,
               &list_id, RESULT_CACHE_REQUIRED, NULL, NULL);

      if (host_variables)
    {
      for (i = 0; i < info[q].host_var_count; i++)
        {
          db_value_clear (&host_variables[i]);
        }
      free (host_variables);
    }

      if (err != NO_ERROR)
    {
      if (err == ER_QPROC_XASLNODE_RECOMPILE_REQUESTED || err == ER_QPROC_INVALID_XASLNODE)
        {
          /* set the flag to recompile from it's main query */
          stmt->info.execute.recompile = 1;
          er_clearid ();
          err = NO_ERROR;
        }
      break;
    }
    }

  return err;
}

/*
 * do_execute_subquery () - execute subquery statements for subquery cache
 * return : Error code
 * parser (in)    : parser context
 * stmt (in) : statement to execute
 * query_flag     : query flag for execution
 */
int
do_execute_subquery (PARSER_CONTEXT * parser, PT_NODE * stmt)
{
  QUERY_ID query_id;
  QFILE_LIST_ID *list_id;
  DB_VALUE *host_variables = NULL;
  CACHE_TIME clt_cache_time;
  int err, i, flag = RESULT_CACHE_REQUIRED;

  CACHE_TIME_RESET (&clt_cache_time);

  if (stmt->sub_host_var_count > 0)
    {
      host_variables = (DB_VALUE *) malloc (sizeof (DB_VALUE) * stmt->sub_host_var_count);
      if (host_variables == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
          sizeof (DB_VALUE) * stmt->sub_host_var_count);
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }

      for (i = 0; i < stmt->sub_host_var_count; i++)
    {
      pr_clone_value (&parser->host_variables[stmt->sub_host_var_index[i]], &host_variables[i]);
    }
    }

  err =
    execute_query (stmt->xasl_id, &query_id, stmt->sub_host_var_count, host_variables, &list_id, flag, &clt_cache_time,
           &stmt->cache_time);

  if (host_variables)
    {
      for (i = 0; i < stmt->sub_host_var_count; i++)
    {
      db_value_clear (&host_variables[i]);
    }
      free (host_variables);
    }

  if (err == ER_QPROC_RESULT_CACHE_INVALID)
    {
      /* retry the statement once */
      if ((err = do_prepare_statement (parser, stmt)) == NO_ERROR)
    {
      err = do_execute_statement (parser, stmt);
    }
    }

  return err;
}

/*
 * do_execute_session_statement () - execute a prepared session statement
 * return :
 * parser (in)    : parser context
 * statement (in) : statement to execute
 */
int
do_execute_session_statement (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int err;
  QFILE_LIST_ID *list_id;
  int query_flag, into_cnt, i, au_save;
  PT_NODE *into;
  const char *into_label;
  DB_VALUE *vals, *v;
  CACHE_TIME clt_cache_time;
  CURSOR_ID cursor_id;
  bool query_trace = false;

  assert (parser->query_id == NULL_QUERY_ID);
  assert (pt_node_to_cmd_type (statement) == CUBRID_STMT_EXECUTE_PREPARE);

  /* check if it is not necessary to execute this statement */
  if (statement->xasl_id == NULL)
    {
      statement->etc = NULL;
      return NO_ERROR;
    }

  if (prm_get_bool_value (PRM_ID_QUERY_TRACE) == true && parser->query_trace == true)
    {
      query_trace = true;
    }

  query_flag = DEFAULT_EXEC_MODE;

  if (parser->flag.is_holdable)
    {
      query_flag |= RESULT_HOLDABLE;
    }
  if (parser->flag.is_xasl_pinned_reference)
    {
      query_flag |= XASL_CACHE_PINNED_REFERENCE;
    }
  if (parser->flag.is_auto_commit)
    {
      query_flag |= TRAN_AUTO_COMMIT;
    }

  if (statement->info.execute.do_cache)
    {
      query_flag |= RESULT_CACHE_REQUIRED;
    }

  if (query_trace == true)
    {
      do_set_trace_to_query_flag (&query_flag);
      do_send_plan_trace_to_session (parser);
    }

  /* flush necessary objects before execute */
  if (ws_has_updated ())
    {
      (void) parser_walk_tree (parser, statement, pt_flush_classes, NULL, NULL, NULL);
    }

  if (parser->flag.abort)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  /* Request that the server executes the stored XASL, which is the execution plan of the prepared query, with the host
   * variables given by users as parameter values for the query. As a result, query id and result file id
   * (QFILE_LIST_ID) will be returned. */

  AU_SAVE_AND_ENABLE (au_save); /* this insures authorization checking for method */
  assert (parser->query_id == NULL_QUERY_ID);
  list_id = NULL;

  CACHE_TIME_RESET (&clt_cache_time);
  if (statement->flag.clt_cache_check)
    {
      clt_cache_time = statement->cache_time;
      statement->flag.clt_cache_check = 0;
    }
  CACHE_TIME_RESET (&statement->cache_time);
  statement->flag.clt_cache_reusable = 0;

  err =
    execute_query (statement->xasl_id, &parser->query_id, parser->host_var_count + parser->auto_param_count,
           parser->host_variables, &list_id, query_flag, &clt_cache_time, &statement->cache_time);

  AU_RESTORE (au_save);

  if (CACHE_TIME_EQ (&clt_cache_time, &statement->cache_time))
    {
      statement->flag.clt_cache_reusable = 1;
    }

  /* save the returned QFILE_LIST_ID into 'statement->etc' */
  statement->etc = (void *) list_id;

  if (err < NO_ERROR)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }
  /* if select ... into label ... has some result val then enter {label,val} pair into the label_table */
  into = statement->info.execute.into_list;
  into_cnt = pt_length_of_list (into);
  if (into_cnt == 0)
    {
      return err;
    }

  vals = (DB_VALUE *) malloc (into_cnt * sizeof (DB_VALUE));
  if (vals == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, into_cnt * sizeof (DB_VALUE));
      return ER_FAILED;
    }
  if (!cursor_open (&cursor_id, list_id, false, statement->info.execute.oids_included))
    {
      free_and_init (vals);
      return err;
    }
  cursor_id.query_id = parser->query_id;
  if (cursor_next_tuple (&cursor_id) != DB_CURSOR_SUCCESS
      || cursor_get_tuple_value_list (&cursor_id, into_cnt, vals) != NO_ERROR)
    {
      free_and_init (vals);
      return err;
    }
  cursor_close (&cursor_id);

  for (i = 0, v = vals; i < into_cnt && into; i++, v++, into = into->next)
    {
      if (into->node_type == PT_NAME && (into_label = into->info.name.original) != NULL)
    {
      err = pt_associate_label_with_value_check_reference (into_label, db_value_copy (v));
    }
      db_value_clear (v);
    }

  free_and_init (vals);

  return err;
}

/*
 * do_execute_select() - Execute the prepared SELECT statement
 *   return: Error code
 *   parser(in/out): Parser context
 *   statement(in/out): A statement to do
 *
 * Note:
 */
int
do_execute_select (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int err = NO_ERROR;
  QFILE_LIST_ID *list_id;
  int query_flag, into_cnt, i, au_save;
  PT_NODE *into;
  const char *into_label;
  DB_VALUE *vals, *v;
  CACHE_TIME clt_cache_time;
  bool query_trace = false;

  assert (parser->query_id == NULL_QUERY_ID);

  /* check if it is not necessary to execute this statement, e.g. false where or not prepared correctly */
  if (!statement->xasl_id)
    {
      statement->etc = NULL;
      return NO_ERROR;
    }

  if (prm_get_bool_value (PRM_ID_QUERY_TRACE) == true && parser->query_trace == true)
    {
      query_trace = true;
    }

  /* adjust query flag */
  query_flag = DEFAULT_EXEC_MODE;

  if (statement->flag.si_datetime == 1 || statement->flag.si_tran_id == 1)
    {
      statement->info.query.flag.reexecute = 1;
      statement->info.query.flag.do_not_cache = 1;
    }

  if (statement->info.query.flag.reexecute == 1)
    {
      query_flag |= NOT_FROM_RESULT_CACHE;
    }

  if (statement->info.query.flag.do_cache == 1)
    {
      query_flag |= RESULT_CACHE_REQUIRED;
    }

  if (statement->info.query.flag.do_not_cache == 1 || statement->info.query.oids_included)
    {
      query_flag |= RESULT_CACHE_INHIBITED;
    }
  if (parser->flag.is_holdable)
    {
      query_flag |= RESULT_HOLDABLE;
    }
  if (parser->flag.is_xasl_pinned_reference)
    {
      query_flag |= XASL_CACHE_PINNED_REFERENCE;
    }

  if (parser->flag.dont_collect_exec_stats)
    {
      query_flag |= DONT_COLLECT_EXEC_STATS;
    }

  if (statement->flag.use_auto_commit)
    {
      query_flag |= EXECUTE_QUERY_WITH_COMMIT;

      if (ws_need_flush ())
    {
      if (tm_Use_OID_preflush)
        {
          (void) locator_assign_all_permanent_oids ();
        }

      /* Flush all dirty objects */
      /* Flush virtual objects first so that locator_all_flush doesn't see any */
      err = locator_all_flush ();
      if (err != NO_ERROR)
        {
          return err;
        }
    }
    }

  if (parser->flag.is_auto_commit)
    {
      query_flag |= TRAN_AUTO_COMMIT;
    }

  if (query_trace == true)
    {
      do_set_trace_to_query_flag (&query_flag);
      do_send_plan_trace_to_session (parser);
    }

  /* flush necessary objects before execute */
  if (ws_has_updated ())
    {
      (void) parser_walk_tree (parser, statement, pt_flush_classes, NULL, NULL, NULL);
    }

  if (parser->flag.abort)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  /* Request that the server executes the stored XASL, which is the execution plan of the prepared query, with the host
   * variables given by users as parameter values for the query. As a result, query id and result file id
   * (QFILE_LIST_ID) will be returned. do_prepare_select() has saved the XASL file id (XASL_ID) in 'statement->xasl_id'
   */

  AU_SAVE_AND_ENABLE (au_save); /* this insures authorization checking for method */

  assert (parser->query_id == NULL_QUERY_ID);
  list_id = NULL;

  CACHE_TIME_RESET (&clt_cache_time);
  if (statement->flag.clt_cache_check)
    {
      clt_cache_time = statement->cache_time;
      statement->flag.clt_cache_check = 0;
    }
  CACHE_TIME_RESET (&statement->cache_time);
  statement->flag.clt_cache_reusable = 0;

  err =
    execute_query (statement->xasl_id, &parser->query_id, parser->host_var_count + parser->auto_param_count,
           parser->host_variables, &list_id, query_flag, &clt_cache_time, &statement->cache_time);

  AU_RESTORE (au_save);

  if (CACHE_TIME_EQ (&clt_cache_time, &statement->cache_time))
    {
      statement->flag.clt_cache_reusable = 1;
    }

  /* save the returned QFILE_LIST_ID into 'statement->etc' */
  statement->etc = (void *) list_id;

  if (err < NO_ERROR)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  /* if SELECT ... INTO label ... has some result val, then enter {label,val} pair into the label_table */
  into = statement->info.query.into_list;
  if ((into_cnt = pt_length_of_list (into)) > 0 && (vals = (DB_VALUE *) malloc (into_cnt * sizeof (DB_VALUE))) != NULL)
    {
      if (pt_get_one_tuple_from_list_id (parser, statement, vals, into_cnt))
    {
      for (i = 0, v = vals; i < into_cnt && into; i++, v++, into = into->next)
        {
          if (into->node_type == PT_NAME && (into_label = into->info.name.original) != NULL)
        {
          err = pt_associate_label_with_value_check_reference (into_label, db_value_copy (v));
        }
          db_value_clear (v);
        }
    }
      else if (into->node_type == PT_NAME)
    {
      PT_ERRORmf (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_PARM_IS_NOT_SET,
              into->info.name.original);
    }
      free_and_init (vals);
    }

  return err;
}               /* do_execute_select() */

static int
do_reserve_oidinfo (PARSER_CONTEXT * parser, PT_NODE * statement, OID ** reserved_oid)
{
  if (prm_get_integer_value (PRM_ID_SUPPLEMENTAL_LOG) != 1)
    {
      return NO_ERROR;
    }

  switch (statement->node_type)
    {
      /* can be expanded to several drop statements */
    case PT_DROP_SERIAL:
      {
    const char *objname = NULL;
    OID *oid = NULL;

    oid = (OID *) malloc (sizeof (OID));
    if (oid == NULL)
      {
        return ER_OUT_OF_VIRTUAL_MEMORY;
      }

    DB_OBJECT *serial_class = sm_find_class (CT_SERIAL_NAME);

    objname = (char *) PT_NODE_SR_NAME (statement);
    if (do_get_serial_obj_id (oid, serial_class, objname) == NULL)
      {
        free_and_init (oid);

        return ER_FAILED;
      }
    *reserved_oid = oid;
    break;
      }
    default:
      break;
    }
  return NO_ERROR;
}

static int
do_find_object_type (PT_MISC_TYPE type, const char *classname, CDC_DDL_OBJECT_TYPE * objtype)
{
  DB_OBJECT *class_obj;

  if (type == PT_CLASS)
    {
      *objtype = CDC_TABLE;
    }
  else if (type == PT_VCLASS)
    {
      *objtype = CDC_VIEW;
    }
  else if (type == PT_MISC_DUMMY)
    {
      class_obj = db_find_class (classname);

      if (db_is_vclass (class_obj))
    {
      *objtype = CDC_VIEW;
    }
      else if (db_is_class (class_obj))
    {
      *objtype = CDC_TABLE;
    }
      else
    {
      return ER_FAILED;
    }
    }
  else
    {
      return ER_FAILED;
    }

  return NO_ERROR;
}

static int
do_reserve_classinfo (PARSER_CONTEXT * parser, PT_NODE * statement, RESERVED_CLASS_INFO ** cls_info)
{
  int count = 0;
  PT_NODE *entity = NULL;
  PT_NODE *entity_spec = NULL;

  const char *classname;
  DB_OBJECT *class_obj;

  if (prm_get_integer_value (PRM_ID_SUPPLEMENTAL_LOG) != 1)
    {
      return NO_ERROR;
    }

  if (statement->node_type == PT_DROP)
    {
      if (statement->info.drop.if_exists && statement->info.drop.spec_list == NULL)
    {
      return NO_ERROR;
    }

      for (entity_spec = statement->info.drop.spec_list; entity_spec != NULL; entity_spec = entity_spec->next)
    {
      entity = entity_spec->info.spec.flat_entity_list;

      cls_info[count] = (RESERVED_CLASS_INFO *) malloc (sizeof (RESERVED_CLASS_INFO));
      if (cls_info[count] == NULL)
        {
          return ER_OUT_OF_VIRTUAL_MEMORY;
        }

      classname = entity->info.name.original;
      class_obj = db_find_class (classname);

      assert ((int) sizeof (cls_info[count]->name) > strlen (classname));
      strcpy (cls_info[count]->name, classname);

      memcpy (&cls_info[count]->oid, ws_oid (class_obj), sizeof (OID));

      if (do_find_object_type (statement->info.drop.entity_type, classname, &cls_info[count]->objtype) != NO_ERROR)
        {
          return ER_FAILED;
        }

      assert (cls_info[count]->objtype == CDC_TABLE || cls_info[count]->objtype == CDC_VIEW);

      count++;
    }
    }

  cls_info[count] = NULL;

  return NO_ERROR;
}

static int
do_supplemental_statement (PARSER_CONTEXT * parser, PT_NODE * statement, RESERVED_CLASS_INFO ** cls_info,
               OID * reserved_oid)
{
  int error = NO_ERROR;
  PARSER_VARCHAR **host_val = NULL;
  char stmt_separator;
  char *stmt_end = NULL;
  char *sbr_text = NULL;
  char *stmt_text = NULL;
  char *drop_stmt = NULL;

  const char *classname = NULL;
  const char *objname = NULL;

  CDC_DDL_TYPE ddl_type;
  CDC_DDL_OBJECT_TYPE objtype;
  PT_NODE *target = NULL;
  OID *classoid = NULL;
  OID oid_tmp = OID_INITIALIZER;
  OID *oid = &oid_tmp;
  OID null_oid = OID_INITIALIZER;


  bool supp_appended = false;

  if (statement->sql_user_text == NULL || statement->sql_user_text_len == 0)
    {
      /* this should be loaddb. */
      return NO_ERROR;
    }

  switch (statement->node_type)
    {
    case PT_CREATE_ENTITY:
      classname = statement->info.create_entity.entity_name->info.name.original;
      ddl_type = CDC_CREATE;
      if (statement->info.create_entity.entity_type == PT_CLASS)
    {
      classname = statement->info.create_entity.entity_name->info.name.original;
      classoid = ws_oid (sm_find_class (classname));
      objtype = CDC_TABLE;
    }
      else if (statement->info.create_entity.entity_type == PT_VCLASS)
    {
      objtype = CDC_VIEW;
    }
      else
    {
      assert (false);
    }

      break;

    case PT_ALTER:
      classname = statement->info.alter.entity_name->info.name.original;
      ddl_type = CDC_ALTER;

      if (do_find_object_type (statement->info.alter.entity_type, classname, &objtype) != NO_ERROR)
    {
      return ER_FAILED;
    }

      assert (objtype == CDC_TABLE || objtype == CDC_VIEW);

      if (objtype == CDC_TABLE)
    {
      classoid = ws_oid (sm_find_class (classname));
    }

      break;

    case PT_RENAME:
      {
    const PT_NODE *current_rename = NULL;
    ddl_type = CDC_RENAME;

    for (current_rename = statement; current_rename != NULL; current_rename = current_rename->next)
      {
        char temp_statement[1024] = "\0";
        char *rename_statement = NULL;
        const char *new_name = current_rename->info.rename.new_name->info.name.original;
        const char *old_name = current_rename->info.rename.old_name->info.name.original;
        int length = 0;

        /* Bug : statement->info.rename.entity_type always has PT_CLASS 
         * when rename view1 as view2 or rename table1 as table2. So, objtype can not be classified with entity_type */
        if (do_find_object_type (PT_MISC_DUMMY, new_name, &objtype) != NO_ERROR)
          {
        error = ER_FAILED;
        goto end;
          }

        assert (objtype == CDC_TABLE || objtype == CDC_VIEW);

        length = strlen (new_name) + strlen (old_name) + 18;

        if (length <= 1024)
          {
        rename_statement = temp_statement;
          }
        else
          {
        rename_statement = (char *) malloc (length);
        if (rename_statement == NULL)
          {
            error = ER_OUT_OF_VIRTUAL_MEMORY;
            goto end;
          }
          }

        if (objtype == CDC_VIEW)
          {
        sprintf (rename_statement, "rename view %s as %s", old_name, new_name);
          }
        else if (objtype == CDC_TABLE)
          {
        classoid = ws_oid (sm_find_class (new_name));
        sprintf (rename_statement, "rename table %s as %s", old_name, new_name);
          }
        else
          {
        assert (false);
          }

        error = log_supplement_statement (ddl_type, objtype, classoid, classoid, rename_statement);

        if (rename_statement != temp_statement)
          {
        free_and_init (rename_statement);
          }
      }

    supp_appended = true;

    break;
      }
    case PT_DROP:
      {
    int drop_stmt_length = 0, pre_drop_length = 0;
    int drop_copied_length = 0, name_len = 0;
    const char *drop_prefix = "drop table ";
    const char *drop_view_prefix = "drop view ";
    const char *if_exist_statement = "if exists ";
    const char *cascade_statement = " cascade constraints";
    const int drop_prefix_len = strlen (drop_prefix);
    const int drop_view_prefix_len = strlen (drop_view_prefix);
    const int if_exist_statement_len = strlen (if_exist_statement);
    const int cascade_statement_len = strlen (cascade_statement);

    if (statement->info.drop.if_exists && statement->info.drop.spec_list == NULL)
      {
        goto end;
      }

    ddl_type = CDC_DROP;
    pre_drop_length =
      MAX (drop_prefix_len, drop_view_prefix_len) + if_exist_statement_len + cascade_statement_len + 2;

    if (cls_info != NULL)
      {
        for (int i = 0; cls_info[i] != NULL; i++)
          {
        name_len = strlen (cls_info[i]->name);
        drop_copied_length = pre_drop_length + name_len;

        if (drop_stmt_length < drop_copied_length)
          {
            if (drop_stmt)
              {
            free (drop_stmt);
              }
            else
              {
            drop_copied_length += 32;   // Ensure sufficient size to avoid reallocation of memory.
              }

            drop_stmt_length = drop_copied_length;
            drop_stmt = (char *) malloc (drop_stmt_length);
            if (drop_stmt == NULL)
              {
            error = ER_OUT_OF_VIRTUAL_MEMORY;
            goto end;
              }
          }

        if (cls_info[i]->objtype == CDC_TABLE)
          {
            memcpy (drop_stmt, drop_prefix, drop_prefix_len);
            drop_copied_length = drop_prefix_len;
          }
        else if (cls_info[i]->objtype == CDC_VIEW)
          {
            memcpy (drop_stmt, drop_view_prefix, drop_view_prefix_len);
            drop_copied_length = drop_view_prefix_len;
          }
        else
          {
            assert (false);
            error = ER_FAILED;
            goto end;
          }

        if (statement->info.drop.if_exists)
          {
            memcpy (drop_stmt + drop_copied_length, if_exist_statement, if_exist_statement_len);
            drop_copied_length += if_exist_statement_len;
          }

        memcpy (drop_stmt + drop_copied_length, cls_info[i]->name, name_len);
        drop_copied_length += name_len;

        if (statement->info.drop.is_cascade_constraints)
          {
            memcpy (drop_stmt + drop_copied_length, cascade_statement, cascade_statement_len);
            drop_copied_length += cascade_statement_len;
          }

        drop_stmt[drop_copied_length] = '\0';

        error =
          log_supplement_statement (ddl_type, cls_info[i]->objtype, &cls_info[i]->oid, &cls_info[i]->oid,
                        drop_stmt);

        free_and_init (cls_info[i]);
          }
      }

    supp_appended = true;

    break;
      }
    case PT_CREATE_INDEX:
      {
    BTID index;
    MOP classop;

    classname = statement->info.index.indexed_class->info.spec.entity_name->info.name.original;
    objname = statement->info.index.index_name->info.name.original;

    classop = sm_find_class (classname);

    classoid = ws_oid (classop);
    error = sm_get_index (classop, objname, &index);
    memcpy (oid, &index, sizeof (OID));

    ddl_type = CDC_CREATE;
    objtype = CDC_INDEX;

    break;
      }
    case PT_ALTER_INDEX:
      {
    BTID index;
    MOP classop;

    classname = statement->info.index.indexed_class->info.spec.entity_name->info.name.original;
    objname = statement->info.index.index_name->info.name.original;

    classop = sm_find_class (classname);

    classoid = ws_oid (classop);
    error = sm_get_index (classop, objname, &index);
    memcpy (oid, &index, sizeof (OID));

    ddl_type = CDC_ALTER;
    objtype = CDC_INDEX;

    break;
      }
    case PT_DROP_INDEX:
      {
    BTID index;
    MOP classop;

    classname = statement->info.index.indexed_class->info.spec.entity_name->info.name.original;
    objname = statement->info.index.index_name->info.name.original;

    classop = sm_find_class (classname);

    classoid = ws_oid (classop);
    error = sm_get_index (classop, objname, &index);
    memcpy (oid, &index, sizeof (OID));

    ddl_type = CDC_DROP;
    objtype = CDC_INDEX;

    break;
      }
    case PT_CREATE_SERIAL:
      {

    DB_OBJECT *serial_class = sm_find_class (CT_SERIAL_NAME);

    objname = (char *) PT_NODE_SR_NAME (statement);
    if (do_get_serial_obj_id (oid, serial_class, objname) == NULL)
      {
        error = ER_FAILED;
        goto end;
      }

    ddl_type = CDC_CREATE;
    objtype = CDC_SERIAL;

    break;
      }
    case PT_ALTER_SERIAL:
      {
    DB_OBJECT *serial_class = sm_find_class (CT_SERIAL_NAME);

    objname = (char *) PT_NODE_SR_NAME (statement);
    if (do_get_serial_obj_id (oid, serial_class, objname) == NULL)
      {
        goto end;
      }

    ddl_type = CDC_ALTER;
    objtype = CDC_SERIAL;

    break;
      }
    case PT_DROP_SERIAL:
      {
    if (reserved_oid != NULL)
      {
        oid = reserved_oid;
      }
    else
      {
        OID_SET_NULL (oid);
      }

    ddl_type = CDC_DROP;
    objtype = CDC_SERIAL;

    break;
      }
    case PT_CREATE_STORED_PROCEDURE:
      ddl_type = CDC_CREATE;
      objtype = CDC_PROCEDURE;
      break;

    case PT_ALTER_STORED_PROCEDURE:
      ddl_type = CDC_ALTER;
      objtype = CDC_PROCEDURE;
      break;

    case PT_DROP_STORED_PROCEDURE:
      ddl_type = CDC_DROP;
      objtype = CDC_PROCEDURE;
      break;

    case PT_CREATE_USER:
      ddl_type = CDC_CREATE;
      objtype = CDC_USER;
      break;

    case PT_ALTER_USER:
      ddl_type = CDC_ALTER;
      objtype = CDC_USER;
      break;

    case PT_DROP_USER:
      ddl_type = CDC_DROP;
      objtype = CDC_USER;
      break;

    case PT_CREATE_TRIGGER:
      target = PT_NODE_TR_TARGET (statement);

      if (target)
    {
      classname = target->info.event_target.class_name->info.name.original;

      classoid = ws_oid (sm_find_class (classname));
    }
      else
    {
      /* Trigger that does not have target (e.g. create trigger.. execute print.. ) */
      classoid = &null_oid;
    }

      ddl_type = CDC_CREATE;
      objtype = CDC_TRIGGER;

      break;

    case PT_RENAME_TRIGGER:
      {
    DB_OBJECT *tr_object;
    TR_TRIGGER *trigger;
    objname = statement->info.rename_trigger.old_name->info.name.original;

    tr_object = tr_find_trigger (objname);
    if (tr_object != NULL)
      {
        trigger = tr_map_trigger (tr_object, true);
        classoid = ws_oid (trigger->class_mop);
      }

    ddl_type = CDC_RENAME;
    objtype = CDC_TRIGGER;

    break;
      }
    case PT_DROP_TRIGGER:
      {
    DB_OBJECT *tr_object;
    TR_TRIGGER *trigger;
    objname =
      statement->info.drop_trigger.trigger_spec_list->info.trigger_spec_list.trigger_name_list->info.name.original;

    tr_object = tr_find_trigger (objname);
    if (tr_object != NULL)
      {
        trigger = tr_map_trigger (tr_object, true);
        classoid = ws_oid (trigger->class_mop);
      }

    ddl_type = CDC_DROP;
    objtype = CDC_TRIGGER;

    break;
      }
    case PT_REMOVE_TRIGGER:
      {
    /* TODO: Further review and action are needed in the future.
     *   This function is called from do_statement() and do_execute_statement().
     *  In the case of PT_REMOVE_TRIGGER, the initialization of ddl_type and objtype is not performed.
     *  As a result, the behavior after exiting the switch statement becomes unclear.
     *  As a temporary measure, assert_release(0); is added.
     */
    assert_release (0);
    error = ER_FAILED;
    goto end;
      }
      break;

    case PT_ALTER_TRIGGER:
      {
    DB_OBJECT *tr_object;
    TR_TRIGGER *trigger;

    objname =
      statement->info.alter_trigger.trigger_spec_list->info.trigger_spec_list.trigger_name_list->info.name.original;

    tr_object = tr_find_trigger (objname);
    if (tr_object != NULL)
      {
        trigger = tr_map_trigger (tr_object, true);
        classoid = ws_oid (trigger->class_mop);
      }

    ddl_type = CDC_ALTER;
    objtype = CDC_TRIGGER;

    break;
      }
    case PT_TRUNCATE:

      assert (statement->info.spec.entity_name);
      classname = statement->info.spec.entity_name->info.spec.entity_name->info.name.original;

      classoid = ws_oid (sm_find_class (classname));
      ddl_type = CDC_TRUNCATE;
      objtype = CDC_TABLE;

      break;
    default:
      return NO_ERROR;
    }

  if (prm_get_integer_value (PRM_ID_SUPPLEMENTAL_LOG) == 2)
    {
      if (ddl_type != CDC_TRUNCATE || objtype != CDC_SERIAL)
    {
      goto end;
    }
    }


  if (parser->host_var_count == 0)
    {
      /* it may contain multiple statements */
      if (strlen (statement->sql_user_text) > statement->sql_user_text_len)
    {
      stmt_end = &statement->sql_user_text[statement->sql_user_text_len];
      stmt_separator = *stmt_end;
      *stmt_end = '\0';
    }
      stmt_text = statement->sql_user_text;
    }
  else
    {
      /*
       * if the query string includes the host variables, while processing the variable holder '?'
       * the values of the host variables can be replaced into the user's original query string
       * the pt_print_db_value(...) returns the value string and its length.
       * the length includes quotes in case of the char string.
       */
      char *sql_text = statement->sql_user_text;
      int sql_len = statement->sql_user_text_len;
      int i, n, nth;
      int var_len = 0;
      bool begin_quote = false;

      host_val = (PARSER_VARCHAR **) malloc (sizeof (PARSER_VARCHAR *) * parser->host_var_count);
      if (host_val == NULL)
    {
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }

      for (i = 0; i < parser->host_var_count; i++)
    {
      host_val[i] = pt_print_db_value (parser, &parser->host_variables[i]);
      var_len += host_val[i]->length;
    }

      sbr_text = (char *) malloc (sql_len + var_len);
      if (sbr_text == NULL)
    {
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto end;
    }

      n = nth = 0;

      for (i = 0; i < sql_len; i++)
    {
      if (sql_text[i] == '\'')
        {
          if (!begin_quote)
        {
          begin_quote = true;
        }
          else
        {
          begin_quote = false;
        }
        }

      if (sql_text[i] == '?' && !begin_quote)
        {
          if (nth < parser->host_var_count)
        {
          strncpy (&sbr_text[n], (char *) host_val[nth]->bytes, host_val[nth]->length);
          n += host_val[nth++]->length;
        }
          else
        {
          error = ER_IT_UNKNOWN_VARIABLE;
          goto end;
        }
        }
      else
        {
          sbr_text[n++] = sql_text[i];
        }
    }

      sbr_text[n] = 0;
      stmt_text = sbr_text;
    }

  if (!supp_appended)
    {
      error = log_supplement_statement (ddl_type, objtype, classoid, oid, stmt_text);
    }

  if (stmt_end != NULL)
    {
      *stmt_end = stmt_separator;
    }

end:

  if (sbr_text)
    {
      free (sbr_text);
    }

  if (host_val)
    {
      free (host_val);
    }

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

  if (cls_info[0] != NULL && statement->node_type == PT_DROP)
    {
      int i = 0;

      while (cls_info[i] != NULL)
    {
      free_and_init (cls_info[i++]);
    }
    }

  return error;
}



/*
 * Function Group:
 * DO Functions for replication management
 *
 */


/*
 * do_replicate_schema() -
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in): The parse tree of a DDL statement
 *
 * Note:
 */
int
do_replicate_statement (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  REPL_INFO repl_info;
  REPL_INFO_SBR repl_stmt;
  PARSER_VARCHAR *name = NULL;
  PARSER_VARCHAR **host_val = NULL;
  static const char *unknown_name = "-";
  char stmt_separator;
  char *stmt_end = NULL;
  char *sbr_text = NULL;

  if (log_does_allow_replication () == false)
    {
      return NO_ERROR;
    }

  if (statement->sql_user_text == NULL || statement->sql_user_text_len == 0)
    {
      /* this should be loaddb. */
      return NO_ERROR;
    }

  switch (statement->node_type)
    {
    case PT_CREATE_ENTITY:
      name = pt_print_bytes (parser, statement->info.create_entity.entity_name);
      repl_stmt.statement_type = CUBRID_STMT_CREATE_CLASS;
      break;

    case PT_ALTER:
      name = pt_print_bytes (parser, statement->info.alter.entity_name);
      repl_stmt.statement_type = CUBRID_STMT_ALTER_CLASS;
      break;

    case PT_RENAME:
      name = pt_print_bytes (parser, statement->info.rename.old_name);
      repl_stmt.statement_type = CUBRID_STMT_RENAME_CLASS;
      break;

    case PT_DROP:
      /* No replication log will be written when there's no applicable table for "drop if exists" */
      if (statement->info.drop.if_exists && statement->info.drop.spec_list == NULL)
    {
      return NO_ERROR;
    }
      repl_stmt.statement_type = CUBRID_STMT_DROP_CLASS;
      break;

    case PT_CREATE_INDEX:
      name = pt_print_bytes (parser, statement->info.index.indexed_class);
      repl_stmt.statement_type = CUBRID_STMT_CREATE_INDEX;
      break;

    case PT_ALTER_INDEX:
      name = pt_print_bytes (parser, statement->info.index.indexed_class);
      repl_stmt.statement_type = CUBRID_STMT_ALTER_INDEX;
      break;

    case PT_DROP_INDEX:
      name = pt_print_bytes (parser, statement->info.index.indexed_class);
      repl_stmt.statement_type = CUBRID_STMT_DROP_INDEX;
      break;

    case PT_CREATE_SERIAL:
      repl_stmt.statement_type = CUBRID_STMT_CREATE_SERIAL;
      break;

    case PT_ALTER_SERIAL:
      repl_stmt.statement_type = CUBRID_STMT_ALTER_SERIAL;
      break;

    case PT_DROP_SERIAL:
      repl_stmt.statement_type = CUBRID_STMT_DROP_SERIAL;
      break;

    case PT_CREATE_STORED_PROCEDURE:
      repl_stmt.statement_type = CUBRID_STMT_CREATE_STORED_PROCEDURE;
      break;

    case PT_ALTER_STORED_PROCEDURE:
      repl_stmt.statement_type = CUBRID_STMT_ALTER_STORED_PROCEDURE;
      break;

    case PT_DROP_STORED_PROCEDURE:
      repl_stmt.statement_type = CUBRID_STMT_DROP_STORED_PROCEDURE;
      break;

    case PT_CREATE_SERVER:
      repl_stmt.statement_type = CUBRID_STMT_CREATE_SERVER;
      break;
    case PT_DROP_SERVER:
      repl_stmt.statement_type = CUBRID_STMT_DROP_SERVER;
      break;

    case PT_RENAME_SERVER:
      repl_stmt.statement_type = CUBRID_STMT_RENAME_SERVER;
      break;

    case PT_ALTER_SERVER:
      repl_stmt.statement_type = CUBRID_STMT_ALTER_SERVER;
      break;

    case PT_ALTER_SYNONYM:
      repl_stmt.statement_type = CUBRID_STMT_ALTER_SYNONYM;
      break;

    case PT_CREATE_SYNONYM:
      repl_stmt.statement_type = CUBRID_STMT_CREATE_SYNONYM;
      break;

    case PT_DROP_SYNONYM:
      repl_stmt.statement_type = CUBRID_STMT_DROP_SYNONYM;
      break;

    case PT_RENAME_SYNONYM:
      repl_stmt.statement_type = CUBRID_STMT_RENAME_SYNONYM;
      break;

    case PT_CREATE_USER:
      repl_stmt.statement_type = CUBRID_STMT_CREATE_USER;
      break;

    case PT_ALTER_USER:
      repl_stmt.statement_type = CUBRID_STMT_ALTER_USER;
      break;

    case PT_DROP_USER:
      repl_stmt.statement_type = CUBRID_STMT_DROP_USER;
      break;

    case PT_GRANT:
      repl_stmt.statement_type = CUBRID_STMT_GRANT;
      break;

    case PT_REVOKE:
      repl_stmt.statement_type = CUBRID_STMT_REVOKE;
      break;

    case PT_CREATE_TRIGGER:
      repl_stmt.statement_type = CUBRID_STMT_CREATE_TRIGGER;
      break;

    case PT_RENAME_TRIGGER:
      repl_stmt.statement_type = CUBRID_STMT_RENAME_TRIGGER;
      break;

    case PT_DROP_TRIGGER:
      repl_stmt.statement_type = CUBRID_STMT_DROP_TRIGGER;
      break;

    case PT_REMOVE_TRIGGER:
      repl_stmt.statement_type = CUBRID_STMT_REMOVE_TRIGGER;
      break;

    case PT_ALTER_TRIGGER:
      repl_stmt.statement_type = CUBRID_STMT_SET_TRIGGER;
      break;

    case PT_TRUNCATE:
      if (!truncate_need_repl_log (statement))
    {
      return NO_ERROR;
    }

      assert (statement->info.spec.entity_name);
      name = pt_print_bytes (parser, statement->info.spec.entity_name->info.spec.entity_name);
      repl_stmt.statement_type = CUBRID_STMT_TRUNCATE;
      break;

    case PT_UPDATE_STATS:
      repl_stmt.statement_type = CUBRID_STMT_UPDATE_STATS;
      break;

    case PT_INSERT:
      repl_stmt.statement_type = CUBRID_STMT_INSERT;
      break;
    case PT_DELETE:
      repl_stmt.statement_type = CUBRID_STMT_DELETE;
      break;
    case PT_UPDATE:
      repl_stmt.statement_type = CUBRID_STMT_UPDATE;
      break;

    case PT_DROP_VARIABLE:  /* DROP VARIABLE statements are not replicated intentionally. */
    default:
      return NO_ERROR;
    }

  repl_info.repl_info_type = REPL_INFO_TYPE_SBR;
  if (name == NULL)
    {
      repl_stmt.name = (char *) unknown_name;
    }
  else
    {
      repl_stmt.name = (char *) pt_get_varchar_bytes (name);
    }

  if (parser->host_var_count == 0)
    {
      /* it may contain multiple statements */
      if (strlen (statement->sql_user_text) > statement->sql_user_text_len)
    {
      stmt_end = &statement->sql_user_text[statement->sql_user_text_len];
      stmt_separator = *stmt_end;
      *stmt_end = '\0';
    }
      repl_stmt.stmt_text = statement->sql_user_text;

      if (statement->node_type == PT_CREATE_SERVER || statement->node_type == PT_ALTER_SERVER)
    {
      /* In the HA process, the original text information is transmitted to the DDL syntax.
       * In the CREATE SERVER statement and the ALTER SEVER statement, 
       * it is necessary to prevent exposure when a password to access another DBMS is passed.
       * For this reason, we change the original information.            
       */
      PARSER_VARCHAR *dblink_str = pt_print_bytes (parser, statement);
      repl_stmt.stmt_text = (char *) pt_get_varchar_bytes (dblink_str);
    }
    }
  else
    {
      /*
       * if the query string includes the host variables, while processing the variable holder '?'
       * the values of the host variables can be replaced into the user's original query string
       * the pt_print_db_value(...) returns the value string and its length.
       * the length includes quotes in case of the char string.
       */
      char *sql_text = statement->sql_user_text;
      int sql_len = statement->sql_user_text_len;
      int i, n, nth;
      int var_len = 0;
      bool begin_quote = false;

      host_val = (PARSER_VARCHAR **) malloc (sizeof (PARSER_VARCHAR *) * parser->host_var_count);
      if (host_val == NULL)
    {
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }

      for (i = 0; i < parser->host_var_count; i++)
    {
      host_val[i] = pt_print_db_value (parser, &parser->host_variables[i]);
      var_len += host_val[i]->length;
    }

      sbr_text = (char *) malloc (sql_len + var_len);
      if (sbr_text == NULL)
    {
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto end;
    }

      n = nth = 0;

      for (i = 0; i < sql_len; i++)
    {
      if (sql_text[i] == '\'')
        {
          if (!begin_quote)
        {
          begin_quote = true;
        }
          else
        {
          begin_quote = false;
        }
        }

      if (sql_text[i] == '?' && !begin_quote)
        {
          if (nth < parser->host_var_count)
        {
          strncpy (&sbr_text[n], (char *) host_val[nth]->bytes, host_val[nth]->length);
          n += host_val[nth++]->length;
        }
          else
        {
          error = ER_IT_UNKNOWN_VARIABLE;
          goto end;
        }
        }
      else
        {
          sbr_text[n++] = sql_text[i];
        }
    }

      sbr_text[n] = 0;
      repl_stmt.stmt_text = sbr_text;
    }

  repl_stmt.db_user = db_get_user_name ();

  if (pt_is_ddl_statement (statement) != 0)
    {
      repl_stmt.sys_prm_context = sysprm_print_parameters_for_ha_repl ();
    }
  else
    {
      repl_stmt.sys_prm_context = NULL;
    }

  assert_release (repl_stmt.db_user != NULL);

  repl_info.info = (char *) &repl_stmt;

  error = locator_flush_replication_info (&repl_info);

  if (stmt_end != NULL)
    {
      *stmt_end = stmt_separator;
    }

  db_string_free (repl_stmt.db_user);

end:
  if (sbr_text)
    {
      free (sbr_text);
    }

  if (host_val)
    {
      free (host_val);
    }

  if (repl_stmt.sys_prm_context)
    {
      free (repl_stmt.sys_prm_context);
    }

  return error;
}





/*
 * Function Group:
 * Implements the scope statement.
 *
 */

/*
 * do_scope() - scopes a statement
 *   return: Error code if scope fails
 *   parser(in/out): Parser context
 *   statement(in): The parse tree of a scope statement
 *
 * Note:
 */
int
do_scope (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  PT_NODE *stmt;

  if (!statement || (statement->node_type != PT_SCOPE) || !((stmt = statement->info.scope.stmt))
      || (stmt->node_type != PT_TRIGGER_ACTION))
    {
      return ER_GENERIC_ERROR;
    }
  else
    {
      switch (stmt->info.trigger_action.action_type)
    {
    case PT_REJECT:
    case PT_INVALIDATE_XACTION:
    case PT_PRINT:
      break;

    case PT_EXPRESSION:
      do_Trigger_involved = true;
#if 0
      if (prm_get_integer_value (PRM_ID_XASL_MAX_PLAN_CACHE_ENTRIES) > 0)
        {

          /* prepare a statement to execute */
          error = do_prepare_statement (parser, stmt->info.trigger_action.expression);
          if (error >= NO_ERROR)
        {
          /* execute the prepared statement */
          error = do_execute_statement (parser, stmt->info.trigger_action.expression);
        }
        }
      else
        {
          error = do_statement (parser, stmt->info.trigger_action.expression);
        }
#else
      error = do_statement (parser, stmt->info.trigger_action.expression);
#endif
      /* Do not reset do_Trigger_involved here. This is intention. */
      break;

    default:
      break;
    }

      return error;
    }
}





/*
 * Function Group:
 * Implements the DO statement.
 *
 */

/*
 * do_execute_do() - execute the DO statement
 *   return: Error code if scope fails
 *   parser(in/out): Parser context
 *   statement(in): The parse tree of the DO statement
 *
 * Note:
 */
int
do_execute_do (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  XASL_NODE *xasl = NULL;
  QFILE_LIST_ID *list_id = NULL;
  int save;
  QUERY_FLAG query_flag;

  XASL_STREAM stream;

  assert (parser->query_id == NULL_QUERY_ID);

  init_xasl_stream (&stream);

  AU_DISABLE (save);
  parser->au_save = save;

  /* mark the beginning of another level of xasl packing */
  pt_enter_packing_buf ();

  /* always sync exec */
  query_flag = DEFAULT_EXEC_MODE;

  /* don't cache anything */
  query_flag |= NOT_FROM_RESULT_CACHE;
  query_flag |= RESULT_CACHE_INHIBITED;

  if (parser->flag.is_auto_commit)
    {
      query_flag |= TRAN_AUTO_COMMIT;
    }

  pt_null_etc (statement);

  /* generate statement's XASL */
  xasl = parser_generate_do_stmt_xasl (parser, statement);

  if (pt_has_error (parser))
    {
      pt_report_to_ersys (parser, PT_EXECUTION);
      error = er_errid ();
      goto end;
    }
  else if (xasl == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }

  if (prm_get_bool_value (PRM_ID_QUERY_TRACE) == true && parser->query_trace == true)
    {
      do_set_trace_to_query_flag (&query_flag);
      do_send_plan_trace_to_session (parser);
    }


  /* map XASL to stream */
  error = xts_map_xasl_to_stream (xasl, &stream);
  if (error != NO_ERROR)
    {
      goto end;
    }

  error =
    prepare_and_execute_query (stream.buffer, stream.buffer_size, &parser->query_id,
                   parser->host_var_count + parser->auto_param_count, parser->host_variables, &list_id,
                   query_flag);

  if (error != NO_ERROR)
    {
      goto end;
    }

  statement->etc = list_id;

end:
  /* free 'stream' that is allocated inside of xts_map_xasl_to_stream() */
  if (stream.buffer)
    {
      free_and_init (stream.buffer);
    }

  /* mark the end of another level of xasl packing */
  pt_exit_packing_buf ();
  AU_ENABLE (save);

  return error;
}

/*
 * do_set_session_variables () - execute a set session variables statement
 * return : error code or no error
 * parser (in)    : parser
 * statement (in) : statement
 */
int
do_set_session_variables (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  DB_VALUE *variables = NULL;
  int count = 0, i = 0;
  PT_NODE *assignment = NULL;

  assert (statement != NULL);
  assert (statement->node_type == PT_SET_SESSION_VARIABLES);

  count = 0;
  /* count assignments */
  assignment = statement->info.set_variables.assignments;
  while (assignment)
    {
      count++;
      assignment = assignment->next;
    }
  /* we will store assignments in an array containing name1, value1, name2, value2... */
  variables = (DB_VALUE *) malloc (count * 2 * sizeof (DB_VALUE));
  if (variables == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, count * 2 * sizeof (DB_VALUE));
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto cleanup;
    }

  /* initialize variables in case we need to exit with error */
  for (i = 0; i < count * 2; i++)
    {
      db_make_null (&variables[i]);
    }

  for (i = 0, assignment = statement->info.set_variables.assignments; assignment; i += 2, assignment = assignment->next)
    {
      pr_clone_value (pt_value_to_db (parser, assignment->info.expr.arg1), &variables[i]);
      pt_evaluate_tree_having_serial (parser, assignment->info.expr.arg2, &variables[i + 1], 1);

      if (pt_has_error (parser))
    {
      /* if error occurred, don't send junk to server */
      pt_report_to_ersys (parser, PT_EXECUTION);
      error = er_errid ();
      goto cleanup;
    }
    }

  error = csession_set_session_variables (variables, count * 2);

cleanup:
  if (variables != NULL)
    {
      for (i = 0; i < count * 2; i++)
    {
      pr_clear_value (&variables[i]);
    }
      free_and_init (variables);
    }
  return error;
}

/*
 * do_drop_session_variables () - execute a drop session variables statement
 * return : error code or no error
 * parser (in)    : parser
 * statement (in) : statement
 */
int
do_drop_session_variables (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  DB_VALUE *values = NULL;
  int count = 0, i = 0;
  PT_NODE *variables = NULL;

  assert (statement != NULL);
  assert (statement->node_type == PT_DROP_SESSION_VARIABLES);

  count = 0;
  /* count assignments */
  variables = statement->info.drop_session_var.variables;
  while (variables)
    {
      count++;
      variables = variables->next;
    }
  /* we will store assignments in an array containing name1, value1, name2, value2... */
  values = (DB_VALUE *) malloc (count * sizeof (DB_VALUE));
  if (values == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, count * sizeof (DB_VALUE));
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto cleanup;
    }

  for (i = 0, variables = statement->info.drop_session_var.variables; variables; i++, variables = variables->next)
    {
      pr_clone_value (pt_value_to_db (parser, variables), &values[i]);
    }

  error = csession_drop_session_variables (values, count);

cleanup:
  if (values != NULL)
    {
      for (i = 0; i < count; i++)
    {
      pr_clear_value (&values[i]);
    }
      free_and_init (values);
    }
  return error;
}

/*
 * MERGE STATEMENT
 */

/* used to generate unique savepoint names */
static int merge_savepoint_number = 0;

/*
 * do_check_merge_trigger() -
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in): Parse tree of a statement
 *
 * Note: The function checks if there is any active trigger with event
 *   TR_EVENT_STATEMENT_INSERT/UPDATE/DELETE defined on the target.
 *   If there is one, raise the trigger. Otherwise, perform the
 *   given do_ function.
 */
int
do_check_merge_trigger (PARSER_CONTEXT * parser, PT_NODE * statement, PT_DO_FUNC * do_func)
{
  int err;

  if (prm_get_bool_value (PRM_ID_BLOCK_NOWHERE_STATEMENT) && statement->info.merge.search_cond == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_BLOCK_NOWHERE_STMT, 0);
      return ER_BLOCK_NOWHERE_STMT;
    }

  if (statement->flag.use_auto_commit)
    {
      /* no active trigger is involved. Avoid lock and fetch request. */
      err = do_func (parser, statement);
    }
  else
    {
      err = check_merge_trigger (do_func, parser, statement);
    }

  return err;
}

/*
 * check_merge_trigger() -
 *   return: Error code
 *   do_func(in): Function to do
 *   parser(in): Parser context used by do_func
 *   statement(in): Parse tree of a statement used by do_func
 *
 * Note: The function checks if there is any active trigger for UPDATE,
 *       INSERT, or DELETE statements of a MERGE statement.
 */
static int
check_merge_trigger (PT_DO_FUNC * do_func, PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int err, result = NO_ERROR;
  TR_STATE *state;
  const char *savepoint_name = NULL;
  PT_NODE *flat = NULL;
  DB_OBJECT *class_ = NULL;

  /* Prepare a trigger state for any triggers that must be raised in this statement */

  state = NULL;

  if (statement->info.merge.into && statement->info.merge.into->info.spec.remote_server_name)
    {
      ;
    }
  else
    {
      flat = (statement->info.merge.into) ? statement->info.merge.into->info.spec.flat_entity_list : NULL;
      class_ = (flat) ? flat->info.name.db_object : NULL;
      if (class_ == NULL)
    {
      PT_INTERNAL_ERROR (parser, "invalid spec id");
      result = ER_FAILED;
      goto exit;
    }

      if (statement->info.merge.update.assignment)
    {
      /* UPDATE statement triggers */
      result = tr_prepare_statement (&state, TR_EVENT_STATEMENT_UPDATE, class_, 0, NULL);
      if (result != NO_ERROR)
        {
          goto exit;
        }
      /* DELETE statement triggers */
      if (statement->info.merge.update.has_delete)
        {
          result = tr_prepare_statement (&state, TR_EVENT_STATEMENT_DELETE, class_, 0, NULL);
          if (result != NO_ERROR)
        {
          goto exit;
        }
        }
    }
      if (statement->info.merge.insert.value_clauses)
    {
      /* INSERT statement triggers */
      result = tr_prepare_statement (&state, TR_EVENT_STATEMENT_INSERT, class_, 0, NULL);
      if (result != NO_ERROR)
        {
          goto exit;
        }
    }
    }

  if (state == NULL)
    {
      /* no triggers */
      result = do_check_internal_statements (parser, statement, do_func);
    }
  else
    {
      /* the operations performed in 'tr_before', 'do_check_internal_statements' and 'tr_after' should be all contained
       * in one transaction */
      if (tr_Current_depth <= 1)
    {
      savepoint_name = mq_generate_name (parser, "UtrP", &tr_savepoint_number);
      if (savepoint_name == NULL)
        {
          result = ER_GENERIC_ERROR;
          goto exit;
        }
      result = tran_system_savepoint (savepoint_name);
      if (result != NO_ERROR)
        {
          goto exit;
        }
    }

      /* fire BEFORE STATEMENT triggers */
      result = tr_before (state);
      if (result == NO_ERROR)
    {
      result = do_check_internal_statements (parser, statement, do_func);
      if (result < NO_ERROR)
        {
          tr_abort (state);
          state = NULL; /* state was freed */
        }
      else
        {
          /* fire AFTER STATEMENT triggers */
          /* try to preserve the usual result value */
          err = tr_after (state);
          if (err != NO_ERROR)
        {
          result = err;
        }
          if (tr_get_execution_state ())
        {
          state = NULL; /* state was freed */
        }
        }
    }
      else
    {
      /* state was freed */
      state = NULL;
    }
    }

exit:
  if (state)
    {
      /* We need to free state and decrease the tr_Current_depth. */
      tr_abort (state);
    }

  if (result < NO_ERROR && savepoint_name != NULL && (result != ER_LK_UNILATERALLY_ABORTED))
    {
      /* savepoint from tran_savepoint() */
      (void) tran_abort_upto_system_savepoint (savepoint_name);
    }
  return result;
}

/*
 * do_merge () - MERGE statement
 *   return:
 *   parser(in):
 *   statement(in):
 *
 */
int
do_merge (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int err = NO_ERROR;
  PT_NODE *not_nulls = NULL, *lhs, *spec = NULL;
  int has_unique;
  const char *savepoint_name = NULL;
  DB_OBJECT *class_obj;
  QFILE_LIST_ID *list_id = NULL;
  PT_NODE *upd_select_stmt = NULL;
  PT_NODE *ins_select_stmt = NULL;
  PT_NODE *select_names = NULL, *select_values = NULL;
  PT_NODE *const_names = NULL, *const_values = NULL;
  PT_NODE *flat, *values_list = NULL;
  PT_NODE **links = NULL;
  PT_NODE *hint_arg;
  QUERY_ID ins_query_id = NULL_QUERY_ID;
  QUERY_ID upd_query_id = NULL_QUERY_ID;
  QUERY_ID query_id_self = parser->query_id;
  int no_vals, no_consts;
  int wait_msecs = -2, old_wait_msecs = -2;
  float hint_waitsecs;
  int result = 0;
  bool insert_only = false;
  PT_NODE *copy_assigns, *save_assigns;

  CHECK_MODIFICATION_ERROR ();

  /* savepoint for statement atomicity */
  savepoint_name = mq_generate_name (parser, "UmsP", &merge_savepoint_number);
  if (savepoint_name == NULL)
    {
      err = ER_GENERIC_ERROR;
      goto exit;
    }
  err = tran_system_savepoint (savepoint_name);
  if (err != NO_ERROR)
    {
      goto exit;
    }

  AU_DISABLE (parser->au_save);

  if (pt_false_where (parser, statement))
    {
      insert_only = true;
      if (!statement->info.merge.insert.value_clauses)
    {
      /* nothing to execute */
      goto exit;
    }
    }

  spec = statement->info.merge.into;
  flat = spec->info.spec.flat_entity_list;
  if (flat == NULL)
    {
      err = ER_GENERIC_ERROR;
      goto exit;
    }
  class_obj = flat->info.name.db_object;

  /* check update part */
  if (statement->info.merge.update.assignment && !insert_only)
    {
      /* check if the target class has UNIQUE constraint */
      err = update_check_for_constraints (parser, &has_unique, &not_nulls, statement);
      /* not needed */
      if (not_nulls)
    {
      parser_free_tree (parser, not_nulls);
      not_nulls = NULL;
    }
      if (err != NO_ERROR)
    {
      goto exit;
    }
      if (has_unique)
    {
      statement->info.merge.flags |= PT_MERGE_INFO_HAS_UNIQUE;
    }

      lhs = statement->info.merge.update.assignment->info.expr.arg1;
      if (PT_IS_N_COLUMN_UPDATE_EXPR (lhs))
    {
      lhs = lhs->info.expr.arg1;
    }
      if (lhs->info.name.meta_class == PT_META_ATTR)
    {
      statement->info.merge.update.do_class_attrs = true;
    }

      if (!statement->info.merge.update.do_class_attrs)
    {
      /* make the SELECT statement for OID list to be updated */
      no_vals = 0;
      no_consts = 0;

      /* make a copy of assignment list to be able to iterate later */
      copy_assigns = parser_copy_tree_list (parser, statement->info.merge.update.assignment);

      err =
        pt_get_assignment_lists (parser, &select_names, &select_values, &const_names, &const_values, &no_vals,
                     &no_consts, statement->info.merge.update.assignment, &links);
      if (err != NO_ERROR)
        {
          parser_free_tree (parser, copy_assigns);
          goto exit;
        }

      /* save assignment list and replace within statement with the copy */
      save_assigns = statement->info.merge.update.assignment;
      statement->info.merge.update.assignment = copy_assigns;

      upd_select_stmt = pt_to_merge_update_query (parser, select_values, &statement->info.merge);

      /* restore assignment list and destroy the copy */
      statement->info.merge.update.assignment = save_assigns;
      parser_free_tree (parser, copy_assigns);

      /* restore tree structure; pt_get_assignment_lists() */
      pt_restore_assignment_links (statement->info.merge.update.assignment, links, -1);

      AU_ENABLE (parser->au_save);
      upd_select_stmt = mq_translate (parser, upd_select_stmt);
      AU_DISABLE (parser->au_save);
      if (upd_select_stmt == NULL)
        {
          err = er_errid ();
          if (err == NO_ERROR)
        {
          if (pt_has_error (parser))
            {
              pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, upd_select_stmt);
              err = er_errid ();
            }
          ASSERT_ERROR_AND_SET (err);
        }
          goto exit;
        }
    }
    }

  /* check insert part */
  if (statement->info.merge.insert.value_clauses)
    {
      PT_NODE *attrs = statement->info.merge.insert.attr_list;

      err = check_for_cons (parser, &has_unique, &not_nulls, attrs, flat->info.name.db_object);
      if (not_nulls)
    {
      parser_free_tree (parser, not_nulls);
      not_nulls = NULL;
    }
      if (err != NO_ERROR)
    {
      goto exit;
    }
      if (has_unique)
    {
      statement->info.merge.flags |= PT_MERGE_INFO_HAS_UNIQUE;
    }

      /* check not nulls attrs are present in attr list */
      err = check_missing_non_null_attrs (parser, spec, attrs, false);
      if (err != NO_ERROR)
    {
      goto exit;
    }

      /* get results from insert's select query */
      if (err >= NO_ERROR && (values_list = statement->info.merge.insert.value_clauses) != NULL)
    {
      ins_select_stmt = pt_to_merge_insert_query (parser, values_list->info.node_list.list, &statement->info.merge);
      AU_ENABLE (parser->au_save);
      ins_select_stmt = mq_translate (parser, ins_select_stmt);
      AU_DISABLE (parser->au_save);

      if (ins_select_stmt == NULL)
        {
          assert (er_errid () != NO_ERROR);
          err = er_errid ();
          if (err == NO_ERROR)
        {
          err = ER_GENERIC_ERROR;
        }
          goto exit;
        }

      ins_select_stmt->etc = NULL;

      /* enable authorization checking during methods in queries */
      AU_ENABLE (parser->au_save);

      query_id_self = parser->query_id;
      parser->query_id = NULL_QUERY_ID;
      err = do_select_for_ins_upd (parser, ins_select_stmt);
      ins_query_id = parser->query_id;
      parser->query_id = query_id_self;

      AU_DISABLE (parser->au_save);

      if (err < NO_ERROR)
        {
          goto exit;
        }

      if (ins_select_stmt->etc == NULL)
        {
          assert (er_errid () != NO_ERROR);
          err = er_errid ();
          if (err == NO_ERROR)
        {
          if (qo_need_skip_execution () == false)
            {
              err = ER_GENERIC_ERROR;
            }
        }

          goto exit;
        }
    }
    }

  /* IX lock on the class */
  if (locator_fetch_class (class_obj, DB_FETCH_CLREAD_INSTWRITE) == NULL)
    {
      assert (er_errid () != NO_ERROR);
      err = er_errid ();
      if (err == NO_ERROR)
    {
      err = ER_GENERIC_ERROR;
    }
      goto exit;
    }

  if (statement->info.merge.update.assignment && !insert_only)
    {
      if (!statement->info.merge.update.do_class_attrs)
    {
      /* flush necessary objects before execute */
      err = sm_flush_objects (class_obj);
      if (err != NO_ERROR)
        {
          goto exit;
        }

      /* enable authorization checking during methods in queries */
      AU_ENABLE (parser->au_save);

      query_id_self = parser->query_id;
      parser->query_id = NULL_QUERY_ID;
      err = do_select_for_ins_upd (parser, upd_select_stmt);
      upd_query_id = parser->query_id;
      parser->query_id = query_id_self;

      AU_DISABLE (parser->au_save);

      if (err < NO_ERROR)
        {
          /* query failed, an error has already been set */
          goto exit;
        }

      list_id = (QFILE_LIST_ID *) upd_select_stmt->etc;
      parser_free_tree (parser, upd_select_stmt);
      upd_select_stmt = NULL;
    }
    }

  hint_arg = statement->info.merge.waitsecs_hint;
  if ((statement->info.merge.hint & PT_HINT_LK_TIMEOUT) && PT_IS_HINT_NODE (hint_arg))
    {
      hint_waitsecs = (float) atof (hint_arg->info.name.original);
      if (hint_waitsecs > 0)
    {
      wait_msecs = (int) (hint_waitsecs * 1000);
    }
      else
    {
      wait_msecs = (int) hint_waitsecs;
    }
      if (wait_msecs >= -1)
    {
      old_wait_msecs = TM_TRAN_WAIT_MSECS ();
      (void) tran_reset_wait_times (wait_msecs);
    }
    }

  /* do update part */
  if (statement->info.merge.update.assignment && !insert_only)
    {
      query_id_self = parser->query_id;
      parser->query_id = upd_query_id;
      if (statement->info.merge.update.do_class_attrs)
    {
      /* update class attributes */
      err = update_class_attributes (parser, statement);
    }
      else
    {
      /* OID list update */
      err = update_objs_for_list_file (parser, list_id, statement, true);
    }

      /* set result count */
      if (err >= NO_ERROR)
    {
      result += err;
    }

      parser->query_id = upd_query_id;
      pt_end_query (parser, query_id_self);
    }

  /* do insert part */
  if (err >= NO_ERROR && (values_list = statement->info.merge.insert.value_clauses) != NULL)
    {
      PT_NODE *save_list;
      PT_MISC_TYPE save_type;

      /* save node list */
      save_type = values_list->info.node_list.list_type;
      save_list = values_list->info.node_list.list;

      values_list->info.node_list.list_type = PT_IS_SUBQUERY;
      values_list->info.node_list.list = ins_select_stmt;

      obt_begin_insert_values ();
      /* execute subquery & insert its results into target class */
      query_id_self = parser->query_id;
      parser->query_id = ins_query_id;
      err = insert_subquery_results (parser, statement, values_list, flat, &savepoint_name);
      parser->query_id = query_id_self;
      if (parser->flag.abort)
    {
      assert (er_errid () != NO_ERROR);
      err = er_errid ();
    }
      else if (err >= NO_ERROR)
    {
      result += err;
    }

      /* restore node list */
      values_list->info.node_list.list_type = save_type;
      values_list->info.node_list.list = save_list;

      parser_free_tree (parser, ins_select_stmt);
      ins_select_stmt = NULL;

      /* pt_end_query() already called by insert_subquery_results() */
    }

  if (old_wait_msecs >= -1)
    {
      (void) tran_reset_wait_times (old_wait_msecs);
    }

  if (err >= NO_ERROR)
    {
      err = db_is_vclass (class_obj);
      if (err > 0)
    {
      err = sm_flush_objects (class_obj);
    }
    }

exit:
  if (upd_select_stmt != NULL)
    {
      parser_free_tree (parser, upd_select_stmt);
    }

  if (list_id != NULL)
    {
      cursor_free_self_list_id (list_id);
      if (upd_query_id != NULL_QUERY_ID && !tran_was_latest_query_ended ())
    {
      qmgr_end_query (upd_query_id);
    }
    }

  if (ins_select_stmt != NULL)
    {
      if (ins_select_stmt->etc != NULL)
    {
      cursor_free_self_list_id (ins_select_stmt->etc);
      if (ins_query_id != NULL_QUERY_ID && !tran_was_latest_query_ended ())
        {
          qmgr_end_query (ins_query_id);
        }
    }
      parser_free_tree (parser, ins_select_stmt);
    }

  if ((err < NO_ERROR) && er_errid () != NO_ERROR)
    {
      pt_record_error (parser, parser->statement_number, statement->line_number, statement->column_number, er_msg (),
               NULL);
    }
  /* If error and a savepoint was created, rollback to savepoint. No need to rollback if the TM aborted the
   * transaction. */
  if (err < NO_ERROR && savepoint_name && err != ER_LK_UNILATERALLY_ABORTED)
    {
      (void) db_abort_to_savepoint (savepoint_name);
    }

  AU_ENABLE (parser->au_save);

  return (err < NO_ERROR) ? err : result;
}

/*
 * do_prepare_merge() - Prepare the MERGE statement
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in/out): Parse tree of a MERGE statement
 *
 */
int
do_prepare_merge (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int err = NO_ERROR;
  PT_NODE *non_nulls_upd = NULL, *non_nulls_ins = NULL, *lhs, *flat = NULL, *spec;
  int has_unique = 0, has_trigger = 0, has_virt = 0, au_save;
  bool server_insert, server_update, server_op, insert_only = false;

  PT_NODE *select_statement = NULL;
  PT_NODE *select_names = NULL, *select_values = NULL;
  PT_NODE *const_names = NULL, *const_values = NULL;
  PT_NODE **links = NULL;
  PT_NODE *default_expr_attrs = NULL;
  DB_OBJECT *class_obj;
  PT_NODE *copy_assigns, *save_assigns;

  int no_vals, no_consts, is_vclass = 0;

  COMPILE_CONTEXT *contextp;
  XASL_STREAM stream;

  contextp = &parser->context;

  init_xasl_stream (&stream);

  if (parser == NULL || statement == NULL)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  contextp->sql_user_text = statement->sql_user_text;
  contextp->sql_user_text_len = statement->sql_user_text_len;

  if (pt_false_where (parser, statement))
    {
      statement->info.merge.flags |= PT_MERGE_INFO_INSERT_ONLY;
      if (!statement->info.merge.insert.value_clauses)
    {
      /* nothing to prepare */
      goto cleanup;
    }
      insert_only = true;
    }

  if (statement->xasl_id)
    {
      /* already prepared */
      goto cleanup;
    }

  if (statement->info.merge.into->info.spec.remote_server_name)
    {
      server_insert = server_update = false;
      if (statement->info.merge.insert.value_clauses)
    {
      server_insert = true;
    }
      if (statement->info.merge.update.assignment && !insert_only)
    {
      server_update = true;
    }
    }
  else
    {
      /* check into for triggers and virtual class */
      AU_SAVE_AND_DISABLE (au_save);

      spec = statement->info.merge.into;
      flat = spec->info.spec.flat_entity_list;
      class_obj = (flat) ? flat->info.name.db_object : NULL;

      if (statement->info.merge.update.assignment && !insert_only)
    {
      err = sm_class_has_triggers (class_obj, &has_trigger, TR_EVENT_UPDATE);
      if (err == NO_ERROR && !has_trigger)
        {
          err = sm_class_has_triggers (class_obj, &has_trigger, TR_EVENT_STATEMENT_UPDATE);
        }
      if (err == NO_ERROR && !has_trigger && statement->info.merge.update.has_delete)
        {
          err = sm_class_has_triggers (class_obj, &has_trigger, TR_EVENT_DELETE);
          if (err == NO_ERROR && !has_trigger)
        {
          err = sm_class_has_triggers (class_obj, &has_trigger, TR_EVENT_STATEMENT_DELETE);
        }
        }
    }
      if (err == NO_ERROR && !has_trigger && statement->info.merge.insert.value_clauses)
    {
      err = sm_class_has_triggers (class_obj, &has_trigger, TR_EVENT_INSERT);
      if (err == NO_ERROR && !has_trigger)
        {
          err = sm_class_has_triggers (class_obj, &has_trigger, TR_EVENT_STATEMENT_INSERT);
        }
    }

      if (err == NO_ERROR)
    {
      is_vclass = db_is_vclass (class_obj);
      if (is_vclass < 0)
        {
          err = is_vclass;
        }
      else
        {
          has_virt = is_vclass || ((flat) ? (flat->info.name.virt_object != NULL) : false);
        }
    }

      AU_RESTORE (au_save);

      if (err != NO_ERROR)
    {
      goto cleanup;
    }

      err = do_evaluate_default_expr (parser, flat);
      if (err != NO_ERROR)
    {
      goto cleanup;
    }

      /* check update part */
      if (statement->info.merge.update.assignment && !insert_only)
    {
      /* check if the target class has UNIQUE constraint */
      err = update_check_for_constraints (parser, &has_unique, &non_nulls_upd, statement);
      if (err != NO_ERROR)
        {
          goto cleanup;
        }
      if (has_unique)
        {
          statement->info.merge.flags |= PT_MERGE_INFO_HAS_UNIQUE;
        }

      server_update = (!has_trigger && !has_virt
               && !update_check_having_meta_attr (parser, statement->info.merge.update.assignment));

      lhs = statement->info.merge.update.assignment->info.expr.arg1;
      if (PT_IS_N_COLUMN_UPDATE_EXPR (lhs))
        {
          lhs = lhs->info.expr.arg1;
        }

      /* if we are updating class attributes, not need to prepare */
      if (lhs->info.name.meta_class == PT_META_ATTR)
        {
          statement->info.merge.update.do_class_attrs = true;
          goto cleanup;
        }
    }
      else
    {
      server_update = !has_trigger && !has_virt;
    }

      /* check insert part */
      if (statement->info.merge.insert.value_clauses)
    {
      PT_NODE *attr, *attrs = statement->info.merge.insert.attr_list;

      if (prm_get_integer_value (PRM_ID_INSERT_MODE) & INSERT_SELECT)
        {
          /* server insert cannot handle insert into a shared attribute */
          server_insert = true;
          attr = attrs;
          while (attr)
        {
          if (attr->node_type != PT_NAME || attr->info.name.meta_class != PT_NORMAL)
            {
              server_insert = false;
              break;
            }
          attr = attr->next;
        }
        }
      else
        {
          server_insert = false;
        }

      err = check_for_cons (parser, &has_unique, &non_nulls_ins, attrs, flat->info.name.db_object);
      if (err != NO_ERROR)
        {
          goto cleanup;
        }
      if (has_unique)
        {
          statement->info.merge.flags |= PT_MERGE_INFO_HAS_UNIQUE;
        }

      /* check not nulls attrs are present in attr list */
      err = check_missing_non_null_attrs (parser, spec, attrs, false);
      if (err != NO_ERROR)
        {
          goto cleanup;
        }
    }
      else
    {
      server_insert = !has_trigger && !has_virt;
    }
    }

  server_op = (server_insert && server_update);

  if (server_op)
    {
      statement->info.merge.flags |= PT_MERGE_INFO_SERVER_OP;

      /* make query string */
      parser->flag.dont_prt_long_string = 1;
      parser->flag.long_string_skipped = 0;
      parser->flag.print_type_ambiguity = 0;
      PT_NODE_PRINT_TO_ALIAS (parser, statement, CUSTOM_PRINT_4_SHA_COMPUTE | PT_PRINT_LOWER);
      contextp->sql_hash_text = (char *) statement->alias_print;
      err = SHA1Compute ((unsigned char *) contextp->sql_hash_text, (unsigned) strlen (contextp->sql_hash_text),
             &contextp->sha1);
      if (err != NO_ERROR)
    {
      ASSERT_ERROR ();
      return err;
    }
      parser->flag.dont_prt_long_string = 0;
      if (parser->flag.long_string_skipped || parser->flag.print_type_ambiguity)
    {
      statement->flag.cannot_prepare = 1;
      statement->info.merge.flags &= ~PT_MERGE_INFO_SERVER_OP;
      goto cleanup;
    }

      /* lookup in XASL cache */
      contextp->recompile_xasl = statement->flag.recompile;
      if (statement->flag.recompile == 0)
    {
      err = prepare_query (contextp, &stream);
      if (err != NO_ERROR)
        {
          ASSERT_ERROR_AND_SET (err);
        }
      else if (contextp->recompile_xasl == true)
        {
          /* recompile requested by server */
          if (stream.xasl_id != NULL)
        {
          free_and_init (stream.xasl_id);
        }
        }
    }

      if (stream.xasl_id == NULL && err == NO_ERROR)
    {
      if (statement->info.merge.insert.value_clauses &&
          (statement->info.merge.into->info.spec.remote_server_name == NULL))
        {
          err = pt_find_omitted_default_expr (parser, flat->info.name.db_object,
                          statement->info.merge.insert.attr_list, &default_expr_attrs);
          if (err != NO_ERROR)
        {
          statement->flag.use_plan_cache = 0;
          statement->xasl_id = NULL;
          goto cleanup;
        }
        }

      /* mark the beginning of another level of xasl packing */
      pt_enter_packing_buf ();

      /* generate MERGE XASL */
      /* Note: Autoparameterization is redundant as the merge XASL generation is already performed. Therefore, this step can be safely omitted. */
      bool backup_flag = parser->flag.is_skip_auto_parameterize;
      parser->flag.is_skip_auto_parameterize = 1;
      contextp->xasl = pt_to_merge_xasl (parser, statement, &non_nulls_upd, &non_nulls_ins, default_expr_attrs);
      parser->flag.is_skip_auto_parameterize = backup_flag;

      stream.buffer = NULL;

      if (contextp->xasl && (err >= NO_ERROR))
        {
          err = xts_map_xasl_to_stream (contextp->xasl, &stream);
          if (err != NO_ERROR)
        {
          PT_ERRORm (parser, statement, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_RESOURCES_EXHAUSTED);
        }

          /* clear constant values */
          if (contextp->xasl->proc.merge.update_xasl)
        {
          int i;
          UPDATE_PROC_NODE *update = &contextp->xasl->proc.merge.update_xasl->proc.update;

          for (i = update->num_assigns - 1; i >= 0; i--)
            {
              if (update->assigns[i].constant)
            {
              pr_clear_value (update->assigns[i].constant);
            }
            }
        }
        }
      else
        {
#if 0               /* TODO */
          assert (er_errid () != NO_ERROR);
#endif
          err = er_errid ();
          if (err != NO_ERROR)
        {
          pt_record_error (parser, parser->statement_number, statement->line_number, statement->column_number,
                   er_msg (), NULL);
        }
        }

      /* cache the XASL */
      if (stream.buffer && (err >= NO_ERROR))
        {
          err = prepare_query (contextp, &stream);
          if (err != NO_ERROR)
        {
          assert (er_errid () != NO_ERROR);
          err = er_errid ();
        }
        }

      /* mark the end of another level of xasl packing */
      pt_exit_packing_buf ();

      /* free 'stream' that is allocated inside of xts_map_xasl_to_stream() */
      if (stream.buffer)
        {
          free_and_init (stream.buffer);
        }
      statement->flag.use_plan_cache = 0;
      statement->xasl_id = stream.xasl_id;
    }
      else
    {
      if (err == NO_ERROR)
        {
          statement->flag.use_plan_cache = 1;
          statement->xasl_id = stream.xasl_id;
        }
      else
        {
          statement->flag.use_plan_cache = 0;
        }

      goto cleanup;
    }
    }
  else
    {
      if (statement->info.merge.update.assignment && !insert_only)
    {
      /* make the SELECT statement for OID list to be updated */
      no_vals = 0;
      no_consts = 0;

      /* make a copy of assignment list to be able to iterate later */
      copy_assigns = parser_copy_tree_list (parser, statement->info.merge.update.assignment);

      err =
        pt_get_assignment_lists (parser, &select_names, &select_values, &const_names, &const_values, &no_vals,
                     &no_consts, statement->info.merge.update.assignment, &links);
      if (err != NO_ERROR)
        {
          parser_free_tree (parser, copy_assigns);
          goto cleanup;
        }

      /* save assignment list and replace within statement with the copy */
      save_assigns = statement->info.merge.update.assignment;
      statement->info.merge.update.assignment = copy_assigns;

      select_statement = pt_to_merge_update_query (parser, select_values, &statement->info.merge);

      /* restore assignment list and destroy the copy */
      statement->info.merge.update.assignment = save_assigns;
      parser_free_tree (parser, copy_assigns);

      /* restore tree structure; pt_get_assignment_lists() */
      pt_restore_assignment_links (statement->info.merge.update.assignment, links, -1);

      AU_SAVE_AND_ENABLE (au_save);
      select_statement = mq_translate (parser, select_statement);
      AU_RESTORE (au_save);
      if (select_statement)
        {
          /* get XASL_ID by calling do_prepare_select() */
          err = do_prepare_select (parser, select_statement);

          /* save the XASL_ID to be used by do_execute_merge() */
          statement->xasl_id = select_statement->xasl_id;
          select_statement->xasl_id = NULL;

          /* deallocate the SELECT statement */
          parser_free_tree (parser, select_statement);
        }
      else
        {
          err = er_errid ();
          if (err == NO_ERROR)
        {
          assert (pt_has_error (parser));

          err = ER_FAILED;
        }
          goto cleanup;
        }
    }

      /* nothing to do for merge insert part */
    }

cleanup:
  if (non_nulls_upd)
    {
      parser_free_tree (parser, non_nulls_upd);
      non_nulls_upd = NULL;
    }
  if (non_nulls_ins)
    {
      parser_free_tree (parser, non_nulls_ins);
      non_nulls_ins = NULL;
    }
  return err;
}

/*
 * do_execute_merge() - Execute the prepared MERGE statement
 *   return: Error code
 *   parser(in): Parser context
 *   statement(in): Parse tree of a MERGE statement
 *
 */
int
do_execute_merge (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int err = NO_ERROR;
  INT64 result = 0;
  int error = NO_ERROR;
  PT_NODE *flat = NULL, *spec = NULL, *values_list = NULL;
  const char *savepoint_name;
  DB_OBJECT *class_obj;
  QFILE_LIST_ID *list_id = NULL;
  int au_save;
  int wait_msecs = -2, old_wait_msecs = -2;
  float hint_waitsecs;
  PT_NODE *ins_select_stmt = NULL, *hint_arg;
  QUERY_ID ins_query_id = NULL_QUERY_ID;
  QUERY_ID query_id_self = parser->query_id;
  bool insert_only = (statement->info.merge.flags & PT_MERGE_INFO_INSERT_ONLY);

  assert (parser->query_id == NULL_QUERY_ID);

  CHECK_MODIFICATION_ERROR ();

  if (insert_only && !statement->info.merge.insert.value_clauses)
    {
      /* nothing to execute */
      goto exit;
    }

  /* savepoint for statement atomicity */
  savepoint_name = mq_generate_name (parser, "UmsP", &merge_savepoint_number);
  if (savepoint_name == NULL)
    {
      err = ER_GENERIC_ERROR;
      goto exit;
    }
  err = tran_system_savepoint (savepoint_name);
  if (err != NO_ERROR)
    {
      goto exit;
    }

  spec = statement->info.merge.into;
  if (spec->info.spec.remote_server_name)
    {
      class_obj = NULL;
    }
  else
    {
      flat = spec->info.spec.flat_entity_list;
      if (flat == NULL)
    {
      err = ER_GENERIC_ERROR;
      goto exit;
    }
      class_obj = flat->info.name.db_object;
    }


  if (statement->info.merge.flags & PT_MERGE_INFO_SERVER_OP)
    {
      /* server side execution */
      int query_flag = DEFAULT_EXEC_MODE;

      /* check if it is not necessary to execute this statement */
      if (statement->xasl_id == NULL)
    {
      statement->etc = NULL;
      goto exit;
    }

      if (parser->flag.is_xasl_pinned_reference)
    {
      query_flag |= XASL_CACHE_PINNED_REFERENCE;
    }

      query_flag |= NOT_FROM_RESULT_CACHE;
      query_flag |= RESULT_CACHE_INHIBITED;

      if (statement->flag.use_auto_commit)
    {
      query_flag |= EXECUTE_QUERY_WITH_COMMIT;
    }

      if (parser->flag.is_auto_commit)
    {
      query_flag |= TRAN_AUTO_COMMIT;
    }

      if (ws_need_flush ())
    {
      // When a transaction is under auto-commit mode, flush all dirty objects to server.
      // Otherwise, flush associated objects.

      if (statement->flag.use_auto_commit)
        {
          err = tran_flush_to_commit ();
        }
      else
        {
          err = sm_flush_objects (class_obj);
        }

      if (err != NO_ERROR)
        {
          // flush error
          goto exit;
        }
    }

      AU_SAVE_AND_ENABLE (au_save); /* this insures authorization checking for method */

      if (statement->info.merge.insert.value_clauses)
    {
      obt_begin_insert_values ();
    }

      assert (parser->query_id == NULL_QUERY_ID);
      list_id = NULL;

      err = execute_query (statement->xasl_id, &parser->query_id, parser->host_var_count + parser->auto_param_count,
               parser->host_variables, &list_id, query_flag, NULL, NULL);

      AU_RESTORE (au_save);
      if (err != NO_ERROR)
    {
      goto exit;
    }

      /* free returned QFILE_LIST_ID */
      if (list_id)
    {
      if ((list_id->tuple_cnt > 0) && class_obj)
        {
          if (statement->flag.use_auto_commit && tran_was_latest_query_committed ())
        {
          /* Nothing to flush. Avoids flush, since may fetch the class. */
          err = sm_decache_instances_after_query_executed_with_commit (class_obj);
        }
          else
        {
          err = sm_flush_and_decache_objects (class_obj, true);
        }

        }
      if (err >= NO_ERROR)
        {
          result += list_id->tuple_cnt;
        }
      cursor_free_self_list_id (list_id);
      list_id = NULL;
    }

      /* end the query; reset query_id and call qmgr_end_query() */
      pt_end_query (parser, query_id_self);
    }
  else
    {
      /* client side execution */

      if (statement->info.merge.update.assignment && !insert_only && !statement->info.merge.update.do_class_attrs)
    {
      int query_flag = DEFAULT_EXEC_MODE;

      /* flush necessary objects before execute */
      err = sm_flush_objects (class_obj);
      if (err != NO_ERROR)
        {
          goto exit;
        }

      if (parser->flag.is_xasl_pinned_reference)
        {
          query_flag |= XASL_CACHE_PINNED_REFERENCE;
        }

      query_flag |= NOT_FROM_RESULT_CACHE;
      query_flag |= RESULT_CACHE_INHIBITED;

      AU_SAVE_AND_ENABLE (au_save); /* this insures authorization checking for method */

      assert (parser->query_id == NULL_QUERY_ID);
      list_id = NULL;
      err =
        execute_query (statement->xasl_id, &parser->query_id, parser->host_var_count + parser->auto_param_count,
               parser->host_variables, &list_id, query_flag, NULL, NULL);
      AU_RESTORE (au_save);
      if (err != NO_ERROR)
        {
          goto exit;
        }
    }

      /* make sure we have a correct lock on the class */
      if (locator_fetch_class (class_obj, DB_FETCH_CLREAD_INSTWRITE) == NULL)
    {
      assert (er_errid () != NO_ERROR);
      err = er_errid ();
      goto exit;
    }

      /* get results from insert's select query */
      if (err >= NO_ERROR && (values_list = statement->info.merge.insert.value_clauses) != NULL)
    {
      ins_select_stmt = pt_to_merge_insert_query (parser, values_list->info.node_list.list, &statement->info.merge);
      AU_SAVE_AND_ENABLE (au_save);
      ins_select_stmt = mq_translate (parser, ins_select_stmt);
      AU_RESTORE (au_save);

      if (ins_select_stmt == NULL)
        {
          err = er_errid ();
          if (err == NO_ERROR)
        {
          assert (pt_has_error (parser));

          err = ER_FAILED;
        }
          goto exit;
        }

      ins_select_stmt->etc = NULL;

      query_id_self = parser->query_id;
      parser->query_id = NULL_QUERY_ID;
      err = do_select (parser, ins_select_stmt);
      ins_query_id = parser->query_id;
      parser->query_id = query_id_self;

      if (err < NO_ERROR)
        {
          goto exit;
        }

      if (ins_select_stmt->etc == NULL)
        {
          assert (er_errid () != NO_ERROR);
          err = er_errid ();
          if (err == NO_ERROR)
        {
          err = ER_GENERIC_ERROR;
        }
          goto exit;
        }
    }

      hint_arg = statement->info.merge.waitsecs_hint;
      if ((statement->info.merge.hint & PT_HINT_LK_TIMEOUT) && PT_IS_HINT_NODE (hint_arg))
    {
      hint_waitsecs = (float) atof (hint_arg->info.name.original);
      if (hint_waitsecs > 0)
        {
          wait_msecs = (int) (hint_waitsecs * 1000);
        }
      else
        {
          wait_msecs = (int) hint_waitsecs;
        }
      if (wait_msecs >= -1)
        {
          old_wait_msecs = TM_TRAN_WAIT_MSECS ();
          (void) tran_reset_wait_times (wait_msecs);
        }
    }

      /* update part */
      if (statement->info.merge.update.assignment && !insert_only)
    {
      AU_SAVE_AND_DISABLE (au_save);

      if (statement->info.merge.update.do_class_attrs)
        {
          /* update class attributes */
          err = update_class_attributes (parser, statement);
        }
      else
        {
          /* OID list update */
          err = update_objs_for_list_file (parser, list_id, statement, true);
        }

      AU_RESTORE (au_save);

      /* set result count */
      if (err >= NO_ERROR)
        {
          result += err;
        }

      if (!statement->info.merge.update.do_class_attrs)
        {
          /* free returned QFILE_LIST_ID */
          if (list_id)
        {
          if (err >= NO_ERROR && list_id->tuple_cnt > 0)
            {
              err = sm_flush_and_decache_objects (class_obj, true);
            }
          cursor_free_self_list_id (list_id);
          list_id = NULL;
        }
        }
      pt_end_query (parser, query_id_self);
    }

      /* insert part */
      if (err >= NO_ERROR && (values_list = statement->info.merge.insert.value_clauses) != NULL)
    {
      PT_NODE *save_list;
      PT_MISC_TYPE save_type;

      /* save node list */
      save_type = values_list->info.node_list.list_type;
      save_list = values_list->info.node_list.list;

      values_list->info.node_list.list_type = PT_IS_SUBQUERY;
      values_list->info.node_list.list = ins_select_stmt;

      AU_SAVE_AND_DISABLE (au_save);

      obt_begin_insert_values ();
      query_id_self = parser->query_id;
      parser->query_id = ins_query_id;
      /* execute subquery & insert its results into target class */
      err = insert_subquery_results (parser, statement, values_list, flat, &savepoint_name);
      parser->query_id = query_id_self;
      if (parser->flag.abort)
        {
          assert (er_errid () != NO_ERROR);
          err = er_errid ();
        }
      else if (err >= NO_ERROR)
        {
          result += err;
        }

      AU_RESTORE (au_save);

      /* restore node list */
      values_list->info.node_list.list_type = save_type;
      values_list->info.node_list.list = save_list;

      parser_free_tree (parser, ins_select_stmt);
      ins_select_stmt = NULL;
    }

      if (old_wait_msecs >= -1)
    {
      (void) tran_reset_wait_times (old_wait_msecs);
    }

      if (err >= NO_ERROR)
    {
      err = db_is_vclass (class_obj);
      if (err > 0)
        {
          err = sm_flush_objects (class_obj);
        }
    }
    }

exit:
  if (ins_select_stmt != NULL)
    {
      if (ins_select_stmt->etc != NULL)
    {
      cursor_free_self_list_id (ins_select_stmt->etc);
      if (ins_query_id != NULL_QUERY_ID && !tran_was_latest_query_ended ())
        {
          qmgr_end_query (ins_query_id);
        }
    }
      parser_free_tree (parser, ins_select_stmt);
    }

  if (list_id != NULL)
    {
      cursor_free_self_list_id (list_id);
    }

  /* If err == er_errid () and parser has error, we already record the parser error to sys error, no need to call
   * pt_record_error (..) */
  if ((err < NO_ERROR) && (error = er_errid ()) != NO_ERROR && (err != error || !pt_has_error (parser)))
    {
      pt_record_error (parser, parser->statement_number, statement->line_number, statement->column_number, er_msg (),
               NULL);
    }
  /* If error and a savepoint was created, rollback to savepoint. No need to rollback if the TM aborted the
   * transaction. */
  if (err < NO_ERROR && savepoint_name && err != ER_LK_UNILATERALLY_ABORTED && statement->flag.use_auto_commit == false)
    {
      db_abort_to_savepoint (savepoint_name);
    }

  return (err < NO_ERROR) ? err : result;
}

/*
 * do_set_names() - Set the client charset and collation.
 *   return: Error code if it fails
 *   parser(in): Parser context
 *   statement(in): Parse tree of a set transaction statement
 *
 * Note:
 */
int
do_set_names (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = ER_GENERIC_ERROR;
  int charset_id, collation_id;

  if (statement->info.set_names.charset_node != NULL)
    {
      error =
    pt_check_grammar_charset_collation (parser, statement->info.set_names.charset_node,
                        statement->info.set_names.collation_node, &charset_id, &collation_id);
    }

  if (error == NO_ERROR)
    {
      /* size of ('intl_collation=') + collation name */
      char sys_prm_chg[15 + COLL_NAME_SIZE];

      snprintf (sys_prm_chg, sizeof (sys_prm_chg) - 1, "intl_collation=%s", lang_get_collation_name (collation_id));
      error = db_set_system_parameters (sys_prm_chg);
    }

  return (error != NO_ERROR) ? ER_OBJ_INVALID_ARGUMENTS : NO_ERROR;
}

/*
 * do_set_timezone() - Set the client timezone.
 *   return: Error code if it fails
 *   parser(in): Parser context
 *   statement(in): Parse tree of a set transaction statement
 *
 * Note:
 */
int
do_set_timezone (PARSER_CONTEXT * parser, PT_NODE * statement)
{
#define MAX_LEN  100
  int error = NO_ERROR;
  PT_NODE *tz_node;
  char *timezone;
  char sys_prm_chg[MAX_LEN];

  tz_node = statement->info.set_timezone.timezone_node;
  assert (statement != NULL && tz_node != NULL && tz_node->info.value.data_value.str != NULL);

  timezone = (char *) tz_node->info.value.data_value.str->bytes;
  snprintf (sys_prm_chg, sizeof (sys_prm_chg) - 1, "timezone=%s", timezone);
  error = db_set_system_parameters (sys_prm_chg);

#undef MAX_LEN
  return (error != NO_ERROR) ? ER_OBJ_INVALID_ARGUMENTS : NO_ERROR;
}

/*
 * do_alter_synonym () - change target or comment of synonym.
 * return: error code
 * parser(in): parser context
 * statement(in): parse tree of a statement
 */
int
do_alter_synonym (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  DB_OBJECT *target_owner_obj = NULL;
  char synonym_name[DB_MAX_IDENTIFIER_LENGTH] = { '\0' };
  const char *target_name = NULL;
  char target_name_buf[DB_MAX_IDENTIFIER_LENGTH] = { '\0' };
  const char *comment = NULL;
  int error = NO_ERROR;

  CHECK_MODIFICATION_ERROR ();
  CHECK_2ARGS_ERROR (parser, statement);

  assert (statement->node_type == PT_ALTER_SYNONYM);

  /* syntax is not supported. */
  assert (PT_SYNONYM_ACCESS_MODIFIER (statement) != PT_PUBLIC);

  /* synonym_name */
  sm_user_specified_name (PT_NAME_ORIGINAL (PT_SYNONYM_NAME (statement)), synonym_name, DB_MAX_IDENTIFIER_LENGTH);

  /* target_name */
  if (PT_SYNONYM_TARGET_NAME (statement) == NULL)
    {
      /* If only the comment is changed, PT_SYNONYM_TARGET_NAME (statement) can be NULL.
       * If both PT_SYNONYM_TARGET_NAME (statement) and PT_SYNONYM_COMMENT (statement) are NULL,
       * an error occurred in yyparse() and it should not have come here. */
      assert (PT_SYNONYM_COMMENT (statement) != NULL);
    }
  else
    {
      /* PT_SYNONYM_TARGET_NAME (statement) != NULL */
      if (PT_SYNONYM_IS_DBLINKED (statement))
    {
      target_name = PT_NAME_ORIGINAL (PT_SYNONYM_TARGET_NAME (statement));
    }
      else
    {
      sm_user_specified_name (PT_NAME_ORIGINAL (PT_SYNONYM_TARGET_NAME (statement)), target_name_buf,
                  DB_MAX_IDENTIFIER_LENGTH);
      target_name = target_name_buf;
    }

      /* target_owner */
      target_owner_obj = db_find_user (PT_NAME_ORIGINAL (PT_SYNONYM_TARGET_OWNER_NAME (statement)));
      if (target_owner_obj == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      return error;
    }
    }

  /* comment */
  if (PT_SYNONYM_COMMENT (statement) != NULL && PT_SYNONYM_COMMENT_STR (statement) != NULL)
    {
      assert ((PT_SYNONYM_COMMENT (statement))->node_type == PT_VALUE);
      comment = (char *) PT_SYNONYM_COMMENT_BYTES (statement);
    }

  error =
    do_alter_synonym_internal (synonym_name, target_name, target_owner_obj, comment, FALSE,
                   PT_SYNONYM_IS_DBLINKED (statement));
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
    }

  return error;
}

/*
 * do_alter_synonym_internal () - alter synonym.
 * return: error code
 * synonym_name(in): synonym name
 * target_name(in): synonym target class name
 * target_owner(in): synonym target class owner
 * comment(in): comments on synonyms
 * is_public_synonym(in): access modifiers for synonyms
 */
static int
do_alter_synonym_internal (const char *synonym_name, const char *target_name, DB_OBJECT * target_owner,
               const char *comment, const int is_public_synonym, bool is_dblinked)
{
  DB_OBJECT *class_obj = NULL;
  DB_OBJECT *instance_obj = NULL;
  DB_OTMPL *obj_tmpl = NULL;
  DB_VALUE value;
  char old_target_name[DB_MAX_IDENTIFIER_LENGTH] = { '\0' };
  DB_OBJECT *old_target_obj = NULL;
  DB_IDENTIFIER *old_target_obj_id = NULL;
  int error = NO_ERROR;
  int save = 0;

  AU_DISABLE (save);

  class_obj = db_find_class (CT_SYNONYM_NAME);
  if (class_obj == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

  db_make_string (&value, synonym_name);
  instance_obj = db_find_unique (class_obj, "unique_name", &value);
  if (instance_obj == NULL)
    {
      ASSERT_ERROR_AND_SET (error);

      if (error == ER_OBJ_OBJECT_NOT_FOUND)
    {
      er_clear ();
      ERROR_SET_WARNING_1ARG (error, ER_SYNONYM_NOT_EXIST, synonym_name);
    }

      goto end;
    }

  /* instance_obj != NULL */
  obj_tmpl = dbt_edit_object (instance_obj);
  if (obj_tmpl == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

  /* old_target_name */
  if (target_name != NULL
      && sm_get_synonym_target_name (instance_obj, old_target_name, DB_MAX_IDENTIFIER_LENGTH) == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

  /* target_unique_name, target_name, target_owner */
  if (target_name == NULL)
    {
      /* If only the comment is changed, target_name can be NULL.
       * If both target_name and comment are NULL,
       * an error occurred in yyparse() and it should not have come here. */
      assert (comment != NULL);
    }
  else if (intl_identifier_casecmp (old_target_name, target_name) != 0)
    {
      /* target_name != NULL */

      /* target_unique_name */
      db_make_string (&value, target_name);
      error = dbt_put_internal (obj_tmpl, "target_unique_name", &value);
      db_value_clear (&value);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

      /* target_name */
      if (is_dblinked)
    {
      db_make_string (&value, target_name);
    }
      else
    {
      db_make_string (&value, sm_remove_qualifier_name (target_name));
    }
      error = dbt_put_internal (obj_tmpl, "target_name", &value);
      db_value_clear (&value);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

      /* target_owner */
      db_make_object (&value, target_owner);
      error = dbt_put_internal (obj_tmpl, "target_owner", &value);
      db_value_clear (&value);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

      old_target_obj = locator_find_class_with_purpose (old_target_name, false);
      old_target_obj_id = ws_identifier (old_target_obj);
    }

  /* comment */
  if (comment != NULL)
    {
      if (*comment == '\0')
    {
      db_make_null (&value);
    }
      else
    {
      db_make_string (&value, comment);
    }
      error = dbt_put_internal (obj_tmpl, "comment", &value);
      db_value_clear (&value);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }
    }

  /* updated_time */
  error = db_update_otmpl_timestamp (obj_tmpl);
  if (error != NO_ERROR)
    {
      goto end;
    }

  instance_obj = dbt_finish_object (obj_tmpl);
  if (instance_obj == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }
  obj_tmpl = NULL;

  error = locator_flush_instance (instance_obj);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
    }

  if (old_target_obj_id != NULL)
    {
      synonym_remove_xasl_by_oid (old_target_obj_id);
    }

end:
  if (obj_tmpl != NULL && instance_obj == NULL)
    {
      dbt_abort_object (obj_tmpl);
    }

  AU_ENABLE (save);

  return error;
}

/*
 * do_create_synonym () - create synonym.
 * return: error code
 * parser(in): parser context
 * statement(in): parse tree of a statement
 */
int
do_create_synonym (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  DB_OBJECT *synonym_obj = NULL;
  DB_OBJECT *synonym_owner_obj = NULL;
  DB_OBJECT *target_owner_obj = NULL;
  char synonym_name[DB_MAX_IDENTIFIER_LENGTH] = { '\0' };
  const char *target_name = NULL;
  char target_name_buf[DB_MAX_IDENTIFIER_LENGTH] = { '\0' };
  const char *comment = NULL;
  int or_replace = FALSE;
  int error = NO_ERROR;

  CHECK_MODIFICATION_ERROR ();
  CHECK_2ARGS_ERROR (parser, statement);

  assert (statement->node_type == PT_CREATE_SYNONYM);

  /* syntax is not supported. */
  assert (PT_SYNONYM_ACCESS_MODIFIER (statement) != PT_PUBLIC);

  /* synonym_name */
  sm_user_specified_name (PT_NAME_ORIGINAL (PT_SYNONYM_NAME (statement)), synonym_name, DB_MAX_IDENTIFIER_LENGTH);

  /* synonym_owner */
  synonym_owner_obj = au_find_user (PT_NAME_ORIGINAL (PT_SYNONYM_OWNER_NAME (statement)));
  if (synonym_owner_obj == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      return error;
    }

  /* or_replace */
  or_replace = PT_SYNONYM_OR_REPLACE (statement);

  /* target_name */
  if (PT_SYNONYM_IS_DBLINKED (statement))
    {
      target_name = PT_NAME_ORIGINAL (PT_SYNONYM_TARGET_NAME (statement));
    }
  else
    {
      sm_user_specified_name (PT_NAME_ORIGINAL (PT_SYNONYM_TARGET_NAME (statement)), target_name_buf,
                  DB_MAX_IDENTIFIER_LENGTH);
      target_name = target_name_buf;
    }

  /* target_owner */
  target_owner_obj = au_find_user (PT_NAME_ORIGINAL (PT_SYNONYM_TARGET_OWNER_NAME (statement)));
  if (target_owner_obj == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      return error;
    }

  /* comment */
  if (PT_SYNONYM_COMMENT (statement) != NULL && PT_SYNONYM_COMMENT_STR (statement) != NULL)
    {
      assert ((PT_SYNONYM_COMMENT (statement))->node_type == PT_VALUE);
      comment = (char *) PT_SYNONYM_COMMENT_BYTES (statement);
    }

  error =
    do_create_synonym_internal (synonym_name, synonym_owner_obj, target_name, target_owner_obj, comment, FALSE,
                or_replace, PT_SYNONYM_IS_DBLINKED (statement));
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
    }

  return error;
}

/*
 * do_create_synonym_internal () - create synonym.
 * return: error code
 * synonym_name(in): synonym name
 * synonym_owner(in): synonym owner
 * target_name(in): synonym target class name
 * target_owner_name(in): synonym target class owner
 * comment(in): comments on synonyms
 * is_public_synonym(in): access modifiers for synonyms
 * or_replace(in): or replace
 */
static int
do_create_synonym_internal (const char *synonym_name, DB_OBJECT * synonym_owner, const char *target_name,
                DB_OBJECT * target_owner, const char *comment, const int is_public_synonym,
                const int or_replace, bool is_dblinked)
{
  DB_OBJECT *class_obj = NULL;
  DB_OBJECT *instance_obj = NULL;
  DB_OTMPL *obj_tmpl = NULL;
  DB_VALUE value;
  int error = NO_ERROR;
  int save = 0;

  AU_DISABLE (save);

  /* synonym class object */
  class_obj = db_find_class (CT_SYNONYM_NAME);
  if (class_obj == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

  db_make_string (&value, synonym_name);
  instance_obj = db_find_unique (class_obj, "unique_name", &value);
  if (instance_obj != NULL)
    {
      if (or_replace == FALSE)
    {
      ERROR_SET_ERROR_1ARG (error, ER_SYNONYM_ALREADY_EXIST, synonym_name);
      goto end;
    }

      /* or_replace == TRUE */
      error = do_drop_synonym_internal (synonym_name, FALSE, FALSE, class_obj, instance_obj);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }
    }
  else
    {
      /* instance_obj == NULL */
      ASSERT_ERROR_AND_SET (error);

      if (error == ER_OBJ_OBJECT_NOT_FOUND)
    {
      er_clear ();
      error = NO_ERROR;
    }
      else
    {
      goto end;
    }

      /* Check if class exists by name. */
      if (db_find_class (synonym_name) != NULL)
    {
      ERROR_SET_ERROR_1ARG (error, ER_LC_CLASSNAME_EXIST, synonym_name);
      goto end;
    }
      else
    {
      /* db_find_class () == NULL */
      ASSERT_ERROR_AND_SET (error);

      if (er_errid () == ER_LC_UNKNOWN_CLASSNAME)
        {
          er_clear ();
          error = NO_ERROR;
        }
      else
        {
          goto end;
        }
    }
    }

  /* (instance_obj != NULL && or_replace == TRUE)
   * || (instance_obj == NULL && db_find_class () == NULL && er_errid () == ER_LC_UNKNOWN_CLASSNAME) */
  obj_tmpl = dbt_create_object (class_obj);
  if (obj_tmpl == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

  /* unique_name */
  db_make_string (&value, synonym_name);
  error = dbt_put_internal (obj_tmpl, "unique_name", &value);
  db_value_clear (&value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  /* synonym_name */
  db_make_string (&value, sm_remove_qualifier_name (synonym_name));
  error = dbt_put_internal (obj_tmpl, "name", &value);
  db_value_clear (&value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  /* synonym_owner */
  db_make_object (&value, synonym_owner);
  error = dbt_put_internal (obj_tmpl, "owner", &value);
  db_value_clear (&value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  /* is_public_synonym */
  db_make_int (&value, is_public_synonym);
  error = dbt_put_internal (obj_tmpl, "is_public", &value);
  db_value_clear (&value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  /* target_unique_name */
  db_make_string (&value, target_name);
  error = dbt_put_internal (obj_tmpl, "target_unique_name", &value);
  db_value_clear (&value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  /* target_name */
  if (is_dblinked)
    {
      db_make_string (&value, target_name);
    }
  else
    {
      db_make_string (&value, sm_remove_qualifier_name (target_name));
    }
  error = dbt_put_internal (obj_tmpl, "target_name", &value);
  db_value_clear (&value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  /* target_owner */
  db_make_object (&value, target_owner);
  error = dbt_put_internal (obj_tmpl, "target_owner", &value);
  db_value_clear (&value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  /* comment */
  if (comment != NULL && *comment != '\0')
    {
      db_make_string (&value, comment);
      error = dbt_put_internal (obj_tmpl, "comment", &value);
      db_value_clear (&value);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }
    }
  /* created_time && updated_time */
  error = db_set_otmpl_timestamps (obj_tmpl);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* flush template */
  instance_obj = dbt_finish_object (obj_tmpl);
  if (instance_obj == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }
  obj_tmpl = NULL;

  error = locator_flush_instance (instance_obj);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
    }

end:
  if (obj_tmpl != NULL && instance_obj == NULL)
    {
      dbt_abort_object (obj_tmpl);
    }

  AU_ENABLE (save);

  return error;
}

/*
 * do_drop_synonym () - drop synonym.
 * return: error code
 * parser(in): parser context
 * statement(in): parse tree of a statement
 */
int
do_drop_synonym (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  DB_OBJECT *synonym_owner_obj = NULL;
  char synonym_name[DB_MAX_IDENTIFIER_LENGTH] = { '\0' };
  int if_exists = FALSE;
  int error = NO_ERROR;

  CHECK_MODIFICATION_ERROR ();
  CHECK_2ARGS_ERROR (parser, statement);

  assert (statement->node_type == PT_DROP_SYNONYM);

  /* syntax is not supported. */
  assert (PT_SYNONYM_ACCESS_MODIFIER (statement) != PT_PUBLIC);

  /* synonym_name */
  sm_user_specified_name (PT_NAME_ORIGINAL (PT_SYNONYM_NAME (statement)), synonym_name, DB_MAX_IDENTIFIER_LENGTH);

  /* if_exists */
  if_exists = PT_SYNONYM_IF_EXISTS (statement);

  error = do_drop_synonym_internal (synonym_name, FALSE, if_exists, NULL, NULL);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
    }

  return error;
}

/*
 * do_drop_synonym_internal () - drop synonym.
 * return: error code
 * synonym_name(in): synonym name
 * is_public_synonym(in): access modifiers for synonyms
 * if_exists(in): if_exists
 * synonym_class_obj(in): NULL or synonym_class_obj
 * synonym_obj(in): NULL or synonym_obj
 */
static int
do_drop_synonym_internal (const char *synonym_name, const int is_public_synonym, const int if_exists,
              DB_OBJECT * synonym_class_obj, DB_OBJECT * synonym_obj)
{
  DB_OBJECT *class_obj = NULL;
  DB_OBJECT *instance_obj = NULL;
  char old_target_name[DB_MAX_IDENTIFIER_LENGTH] = { '\0' };
  DB_OBJECT *old_target_obj = NULL;
  DB_IDENTIFIER *old_target_obj_id = NULL;
  DB_VALUE value;
  int error = NO_ERROR;
  int save = 0;

  AU_DISABLE (save);

  if (synonym_class_obj != NULL)
    {
      class_obj = synonym_class_obj;
    }
  else
    {
      /* synonym_class_obj == NULL */
      class_obj = db_find_class (CT_SYNONYM_NAME);
      if (class_obj == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }
    }

  if (synonym_obj != NULL)
    {
      instance_obj = synonym_obj;
    }
  else
    {
      /* synonym_obj == NULL */
      db_make_string (&value, synonym_name);
      instance_obj = db_find_unique (class_obj, "unique_name", &value);
      if (instance_obj == NULL)
    {
      ASSERT_ERROR_AND_SET (error);

      if (error == ER_OBJ_OBJECT_NOT_FOUND)
        {
          er_clear ();
          error = NO_ERROR;
        }
      else
        {
          goto end;
        }

      if (if_exists == FALSE)
        {
          ERROR_SET_ERROR_1ARG (error, ER_SYNONYM_NOT_EXIST, synonym_name);
        }

      goto end;
    }
    }

  if (sm_get_synonym_target_name (instance_obj, old_target_name, DB_MAX_IDENTIFIER_LENGTH) == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }
  old_target_obj = locator_find_class_with_purpose (old_target_name, false);
  old_target_obj_id = ws_identifier (old_target_obj);

  error = db_drop (instance_obj);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  error = locator_flush_instance (instance_obj);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
    }

  if (old_target_obj_id != NULL)
    {
      synonym_remove_xasl_by_oid (old_target_obj_id);
    }

end:
  AU_ENABLE (save);

  return error;
}

/*
 * do_rename_synonym () - rename synonym.
 * return: error code
 * parser(in): parser context
 * statement(in): parse tree of a statement
 */
int
do_rename_synonym (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  char old_synonym_name[DB_MAX_IDENTIFIER_LENGTH] = { '\0' };
  char new_synonym_name[DB_MAX_IDENTIFIER_LENGTH] = { '\0' };
  int error = NO_ERROR;

  CHECK_MODIFICATION_ERROR ();
  CHECK_2ARGS_ERROR (parser, statement);

  assert (statement->node_type == PT_RENAME_SYNONYM);

  /* syntax is not supported. */
  assert (PT_SYNONYM_ACCESS_MODIFIER (statement) != PT_PUBLIC);

  /* old_synonym_name */
  sm_user_specified_name (PT_NAME_ORIGINAL (PT_SYNONYM_OLD_NAME (statement)), old_synonym_name,
              DB_MAX_IDENTIFIER_LENGTH);

  /* new_synonym_name */
  sm_user_specified_name (PT_NAME_ORIGINAL (PT_SYNONYM_NEW_NAME (statement)), new_synonym_name,
              DB_MAX_IDENTIFIER_LENGTH);

  error = do_rename_synonym_internal (old_synonym_name, new_synonym_name);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
    }

  return error;
}

/*
 * do_rename_synonym_internal () - rename synonym.
 * return: error code
 * old_synonym_name(in): old synonym name
 * new_synonym_name(in): new synonym name
 */
static int
do_rename_synonym_internal (const char *old_synonym_name, const char *new_synonym_name)
{
  DB_OBJECT *class_obj = NULL;
  DB_OBJECT *instance_obj = NULL;
  DB_OBJECT *new_instance_obj = NULL;
  DB_OTMPL *obj_tmpl = NULL;
  DB_VALUE value;
  char old_target_name[DB_MAX_IDENTIFIER_LENGTH] = { '\0' };
  DB_OBJECT *old_target_obj = NULL;
  DB_IDENTIFIER *old_target_obj_id = NULL;
  int error = NO_ERROR;
  int save = 0;

  AU_DISABLE (save);

  class_obj = db_find_class (CT_SYNONYM_NAME);
  if (class_obj == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

  db_make_string (&value, old_synonym_name);
  instance_obj = db_find_unique (class_obj, "unique_name", &value);
  if (instance_obj == NULL)
    {
      ASSERT_ERROR_AND_SET (error);

      if (error == ER_OBJ_OBJECT_NOT_FOUND)
    {
      er_clear ();
      ERROR_SET_WARNING_1ARG (error, ER_SYNONYM_NOT_EXIST, old_synonym_name);
    }

      goto end;
    }

  /* instance_obj != NULL */
  db_make_string (&value, new_synonym_name);
  new_instance_obj = db_find_unique (class_obj, "unique_name", &value);
  if (new_instance_obj != NULL)
    {
      ERROR_SET_ERROR_1ARG (error, ER_SYNONYM_ALREADY_EXIST, new_synonym_name);
      goto end;
    }
  else
    {
      /* new_instance_obj == NULL */
      ASSERT_ERROR_AND_SET (error);

      if (error == ER_OBJ_OBJECT_NOT_FOUND)
    {
      er_clear ();
      error = NO_ERROR;
    }
      else
    {
      goto end;
    }

      /* Check if class exists by name. */
      if (db_find_class (new_synonym_name) != NULL)
    {
      ERROR_SET_ERROR_1ARG (error, ER_LC_CLASSNAME_EXIST, new_synonym_name);
      goto end;
    }
      else
    {
      ASSERT_ERROR_AND_SET (error);

      if (error == ER_LC_UNKNOWN_CLASSNAME)
        {
          er_clear ();
          error = NO_ERROR;
        }
      else
        {
          goto end;
        }
    }
    }

  /* instance_obj != NULL && new_instance_obj == NULL && er_errid () == ER_LC_UNKNOWN_CLASSNAME */
  obj_tmpl = dbt_edit_object (instance_obj);
  if (obj_tmpl == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

  /* unique_name */
  db_make_string (&value, new_synonym_name);
  error = dbt_put_internal (obj_tmpl, "unique_name", &value);
  db_value_clear (&value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  /* name */
  db_make_string (&value, sm_remove_qualifier_name (new_synonym_name));
  error = dbt_put_internal (obj_tmpl, "name", &value);
  db_value_clear (&value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  /* updated_time */
  error = db_update_otmpl_timestamp (obj_tmpl);
  if (error != NO_ERROR)
    {
      goto end;
    }

  if (sm_get_synonym_target_name (instance_obj, old_target_name, DB_MAX_IDENTIFIER_LENGTH) == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }
  old_target_obj = locator_find_class_with_purpose (old_target_name, false);
  old_target_obj_id = ws_identifier (old_target_obj);

  instance_obj = dbt_finish_object (obj_tmpl);
  if (instance_obj == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }
  obj_tmpl = NULL;

  error = locator_flush_instance (instance_obj);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
    }

  if (old_target_obj_id != NULL)
    {
      synonym_remove_xasl_by_oid (old_target_obj_id);
    }

end:
  if (obj_tmpl != NULL && instance_obj == NULL)
    {
      dbt_abort_object (obj_tmpl);
    }

  AU_ENABLE (save);

  return error;
}

/*
 * pt_append_odku_references () - append references to SELECT specs from
 *                on duplicate key assignments to the SELECT
 *                list
 * return :
 * parser (in) :
 * node (in) :
 * arg (in) :
 * continue_walk (in) :
 *
 * Note: This function rewrites the update assignments of an
 *  INSERT...SELECT... ON DUPLICATE KEY UPDATE statement so that they can be
 *  evaluated as constants. In order for this to be done, all right hand
 *  references to attributes from the SELECT specs are added to the
 *  SELECT list and replaced in assignments with PT_TUPLE_VALUE nodes. We can
 *  only handle PT_NAME and PT_DOT_ (PT_NAME.PT_NAME) nodes here. We should
 *  not encounter complex path expressions here but we will not throw an error
 *  if we do. Complex path expression will either be foldable or we will throw
 *  an error when we try to evaluate them.
 */
static PT_NODE *
pt_append_odku_references (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  ODKU_TUPLE_VALUE_ARG *odku_arg;
  PT_NODE *insert_spec = NULL;
  PT_NODE *select = NULL, *select_list = NULL, *select_spec = NULL, *spec = NULL;
  PT_NODE *name_node = NULL;

  *continue_walk = PT_CONTINUE_WALK;

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

  odku_arg = (ODKU_TUPLE_VALUE_ARG *) arg;

  if (odku_arg->insert_stmt->node_type != PT_INSERT)
    {
      assert (false);
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      *continue_walk = PT_STOP_WALK;
      return NULL;
    }

  if (odku_arg->insert_stmt->info.insert.value_clauses->node_type != PT_NODE_LIST)
    {
      *continue_walk = PT_STOP_WALK;
      return node;
    }

  insert_spec = odku_arg->insert_stmt->info.insert.spec;

  select = odku_arg->insert_stmt->info.insert.value_clauses->info.node_list.list;
  if (!PT_IS_SELECT (select))
    {
      *continue_walk = PT_STOP_WALK;
      return node;
    }

  select_list = pt_get_select_list (parser, select);
  if (select_list == NULL)
    {
      assert (false);
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      return NULL;
    }

  if (node->node_type == PT_NAME)
    {
      name_node = node;
    }
  else if (node->node_type == PT_DOT_)
    {
      if (node->info.dot.arg1 == NULL || node->info.dot.arg1->node_type != PT_NAME)
    {
      return node;
    }

      /* do not visit leaves */
      *continue_walk = PT_LIST_WALK;
      name_node = node->info.dot.arg1;
    }
  else
    {
      return node;
    }

  if (name_node->info.name.spec_id == insert_spec->info.spec.id)
    {
      /* node belongs to the insert class, nothing to be done here */
      return node;
    }

  select_spec = select->info.query.q.select.from;
  for (spec = select_spec; spec != NULL; spec = spec->next)
    {
      if (name_node->info.name.spec_id == spec->info.spec.id)
    {
      *continue_walk = PT_LIST_WALK;
      return pt_make_tuple_value_reference (parser, node, select_list, odku_arg->cursor_p);
    }
    }

  /* There might be names which do not belong to either the select specs or the insert specs: ON DUPLICATE UPDATE set
   * column = (SELECT ...) */
  return node;
}

/*
 * do_evaluate_insert_values () - Evaluates list of values for insert.
 *
 * return        : Error code.
 * parser (in)       : Parser context.
 * insert_statement (in) : Parse tree node for insert statement.
 *
 * NOTE: The values corresponding to each attribute are evaluated from "left"
 *   to "right", in the order given by user.
 *   First step is to replace attribute names with values (see
 *   do_replace_names_for_insert_values_pre).
 *   Nodes in values list will be replaced with PT_INSERT_VALUES nodes
 *   that store the original node (for reevaluation) and the evaluated
 *   value.
 */
static int
do_evaluate_insert_values (PARSER_CONTEXT * parser, PT_NODE * insert_statement)
{
  PT_NODE *val = NULL, *prev = NULL;
  PT_NODE *result = NULL, *save_next = NULL;
  EVAL_INSERT_VALUE eval;
  DB_VALUE eval_value;
  PT_NODE *attr_list = NULL, *value_list = NULL, *value_clause = NULL;
  PT_NODE *temp = NULL;
  bool free_temp = false;

  attr_list = insert_statement->info.insert.attr_list;
  value_clause = insert_statement->info.insert.value_clauses;
  if (attr_list == NULL || value_clause == NULL || value_clause->info.node_list.list_type == PT_IS_SUBQUERY)
    {
      /* nothing to evaluate */
      return NO_ERROR;
    }

  /* initialize attr_list in EVAL_INSERT_VALUE object */
  eval.spec_id = insert_statement->info.insert.spec->info.spec.id;
  eval.attr_list = attr_list;
  eval.value_list = NULL;
  eval.reevaluate_needed = false;
  eval.replace_names = false;
  eval.crt_attr_index = 0;

  db_make_null (&eval_value);

  /* evaluate lists of values */
  for (value_list = value_clause; value_list != NULL; value_list = value_list->next)
    {
      if (value_list->info.node_list.list_type != PT_IS_VALUE)
    {
      continue;
    }
      assert (value_list->node_type == PT_NODE_LIST);
      assert (value_list->info.node_list.list_type == PT_IS_VALUE);
      eval.value_list = value_list->info.node_list.list;
      eval.crt_attr_index = 0;

      /* evaluate values in val_list */
      for (prev = NULL, val = value_list->info.node_list.list; val != NULL; val = save_next, eval.crt_attr_index++)
    {
      save_next = val->next;

      if (PT_IS_INSERT_VALUE_NODE (val))
        {
          /* this is not the first evaluation */
          if (val->info.insert_value.is_evaluated)
        {
          /* already evaluated, do nothing */
          continue;
        }
          /* need to reevaluate */
          if (val->info.insert_value.replace_names)
        {
          /* duplicate original node because it will be altered */
          temp = parser_copy_tree (parser, val->info.insert_value.original_node);
          if (temp == NULL)
            {
              if (!pt_has_error (parser))
            {
              PT_ERRORmf (parser, val, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_OUT_OF_MEMORY,
                      sizeof (PT_NODE));
            }
              goto end_error;
            }
          free_temp = true;
          /* replace names */
          temp =
            parser_walk_tree (parser, temp, do_replace_names_for_insert_values_pre, &eval, pt_continue_walk,
                      NULL);
          if (pt_has_error (parser))
            {
              goto end_error;
            }
        }
          else
        {
          temp = val->info.insert_value.original_node;
        }
          /* obtain evaluated value */
          pt_evaluate_tree_having_serial (parser, temp, &val->info.insert_value.value, 1);
          if (free_temp)
        {
          /* free temp */
          parser_free_tree (parser, temp);
          temp = NULL;
          free_temp = false;
        }
          if (pt_has_error (parser))
        {
          /* evaluation failed */
          goto end_error;
        }
          val->info.insert_value.is_evaluated = true;
          /* evaluation ok, continue to next value */
          continue;
        }

      /* this must be first evaluation */
      val->next = NULL;
      eval.reevaluate_needed = false;
      eval.replace_names = false;

      if (!PT_IS_CONST (val))
        {
          /* duplicate value as it may be altered when names are replaced */
          temp = parser_copy_tree (parser, val);

          free_temp = true;
          temp =
        parser_walk_tree (parser, temp, do_replace_names_for_insert_values_pre, &eval, pt_continue_walk, NULL);
          if (pt_has_error (parser))
        {
          /* error replacing names */
          val->next = save_next;
          goto end_error;
        }
        }
      else
        {
          temp = val;
          free_temp = false;
          if (!PT_IS_VALUE_NODE (temp))
        {
          eval.reevaluate_needed = true;
        }
        }

      assert (temp != NULL);
      /* evaluate temp to obtain a value */
      db_value_clear (&eval_value);
      pt_evaluate_tree_having_serial (parser, temp, &eval_value, 1);
      if (pt_has_error (parser))
        {
          val->next = save_next;
          goto end_error;
        }

      if (free_temp)
        {
          parser_free_tree (parser, temp);
          temp = NULL;
          free_temp = false;
        }

      result = pt_insert_value (parser, NULL);
      if (result == NULL)
        {
          if (!pt_has_error (parser))
        {
          PT_ERRORmf (parser, val, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
                  pt_short_print (parser, val));
        }
          val->next = save_next;
          goto end_error;
        }
      if (db_value_clone (&eval_value, &result->info.insert_value.value) != NO_ERROR)
        {
          PT_ERRORmf (parser, val, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
              pt_short_print (parser, val));
          parser_free_tree (parser, result);
          val->next = save_next;
          goto end_error;
        }
      result->info.insert_value.is_evaluated = true;
      if (eval.reevaluate_needed || PT_IS_VALUE_NODE (val))
        {
          /* save original node */
          result->info.insert_value.original_node = val;
        }
      else
        {
          /* save a PT_VALUE node instead of original node in order to avoid reevaluation */
          result->info.insert_value.original_node = pt_dbval_to_value (parser, &eval_value);
          if (result->info.insert_value.original_node == NULL)
        {
          if (!pt_has_error (parser))
            {
              PT_ERRORmf (parser, val, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
                  pt_short_print (parser, val));
            }
          parser_free_tree (parser, result);
          val->next = save_next;
          goto end_error;
        }
        }
      result->info.insert_value.replace_names = eval.replace_names;

      result->line_number = val->line_number;
      result->column_number = val->column_number;

      /* replace val */
      result->next = save_next;

      if (prev == NULL)
        {
          eval.value_list = value_list->info.node_list.list = result;
        }
      else
        {
          prev->next = result;
        }
      prev = result;
    }
      pr_clear_value (&eval_value);
    }
  return NO_ERROR;

end_error:
  pr_clear_value (&eval_value);
  if (temp != NULL && free_temp)
    {
      parser_free_tree (parser, temp);
    }
  if (er_errid () != NO_ERROR)
    {
      return er_errid ();
    }
  return ER_FAILED;
}

/*
 * do_clear_insert_values () - Resets PT_INSERT_VALUE nodes in value clauses
 *                 to be reevaluation on next execution.
 *
 * return        : Void.
 * parser (in)       : Parser context.
 * insert_statement (in) : Parse tree node for insert statement.
 *
 * Note: Only some nodes are reseted. If the evaluated value is never changing
 *   the node remains "evaluated".
 */
static void
do_clear_insert_values (PARSER_CONTEXT * parser, PT_NODE * insert_statement)
{
  PT_NODE *value_list = NULL, *value = NULL;
  if (insert_statement == NULL || insert_statement->node_type != PT_INSERT)
    {
      return;
    }
  if (insert_statement->info.insert.value_clauses == NULL)
    {
      return;
    }

  for (value_list = insert_statement->info.insert.value_clauses; value_list != NULL; value_list = value_list->next)
    {
      if (value_list->info.node_list.list_type != PT_IS_VALUE)
    {
      continue;
    }
      for (value = value_list->info.node_list.list; value != NULL; value = value->next)
    {
      if (PT_IS_INSERT_VALUE_NODE (value) && !PT_IS_VALUE_NODE (value->info.insert_value.original_node))
        {
          /* prepare node for reevaluation */
          value->info.insert_value.is_evaluated = false;
          db_value_clear (&value->info.insert_value.value);
          db_make_null (&value->info.insert_value.value);
        }
    }
    }
}

/*
 * insert_rewrite_names_in_value_clauses () - Rewrites some names in insert
 *                        VALUE clause.
 *
 * return        : void.
 * parser (in)       : Parser context.
 * insert_statement (in) : Insert statement.
 *
 * NOTE: Names that are replaced:
 *   1. References to attribute from insert spec that are unassigned are
 *      replaced with default values.
 *      e.g.: INSERT INTO t (a, b) VALUES (b, a + 1) => In a = b, b is not
 *        assigned yet and is replaced with DEFAULT(b). In b = a + 1,
 *        a is assigned and is not replaced here.
 *   2. META_CLASS names are replaced with class object.
 *   3. PT_PARAMETER names that point to object type values are replaced.
 */
void
insert_rewrite_names_in_value_clauses (PARSER_CONTEXT * parser, PT_NODE * insert_statement)
{
  PT_NODE *attr_list = NULL, *value_clauses = NULL, *value_list = NULL;
  PT_NODE *value = NULL, *value_tmp = NULL, *save_next = NULL, *prev = NULL;

  EVAL_INSERT_VALUE eval;
  if (insert_statement == NULL || insert_statement->node_type != PT_INSERT)
    {
      return;
    }

  attr_list = insert_statement->info.insert.attr_list;
  value_clauses = insert_statement->info.insert.value_clauses;
  if (attr_list == NULL || value_clauses == NULL)
    {
      return;
    }

  eval.spec_id = insert_statement->info.insert.spec->info.spec.id;
  eval.attr_list = attr_list;
  eval.value_list = NULL;

  for (value_list = value_clauses; value_list != NULL; value_list = value_list->next)
    {
      if (value_list->info.node_list.list_type != PT_IS_VALUE)
    {
      continue;
    }
      prev = NULL;
      for (value = value_list->info.node_list.list, eval.crt_attr_index = 0; value != NULL;
       value = save_next, eval.crt_attr_index++)
    {
      save_next = value->next;
      if (PT_IS_VALUE_NODE (value) || PT_IS_HOSTVAR (value))
        {
          prev = value;
          continue;
        }
      value->next = NULL;

      value = parser_walk_tree (parser, value, do_replace_names_for_insert_values_pre, &eval, NULL, NULL);
      if (!pt_has_error (parser))
        {
          value_tmp = pt_semantic_type (parser, value, NULL);
          if (value_tmp == NULL)
        {
          /* In this case, pt_has_error (parser) is true, we need recovery the link list firstly, then return. */
          ;
        }
          else
        {
          value = value_tmp;
        }
        }
      value->next = save_next;
      if (prev == NULL)
        {
          value_list->info.node_list.list = value;
        }
      else
        {
          prev->next = value;
        }
      if (pt_has_error (parser))
        {
          return;
        }
      prev = value;
    }
    }
}

/*
 * do_replace_names_for_insert_values_pre () - Used by parser_walk_tree to
 *                         evaluate names in insert values
 *
 * return         : node or replaced name .
 * parser (in)        : parser context.
 * node (in)          : node in parse tree.
 * arg (in)       : EVAL_INSERT_VALUE.
 * continue_walk (in) : continue walk.
 *
 * NOTE: Name replacement will be done after the next rules:
 *   1. If name belongs to insert attribute list and if name was assigned
 *      before current attribute, use the assigned value to replace name.
 *   2. If name was not assigned yet, replace it with default value.
 *   3. If could not find a default value for name, then
 *      pt_evaluate_tree_having_serial will have to evaluate the name.
 */
static PT_NODE *
do_replace_names_for_insert_values_pre (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  int count, found, error = NO_ERROR;
  PT_NODE *attr = NULL, *val = NULL, *result = NULL;
  EVAL_INSERT_VALUE *eval = (EVAL_INSERT_VALUE *) arg;
  DB_OBJECT *obj;
  DB_VALUE db_value;
  int is_class = 0;

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

  switch (node->node_type)
    {
    case PT_NAME:
      *continue_walk = PT_LIST_WALK;
      if (node->info.name.spec_id != eval->spec_id)
    {
      eval->reevaluate_needed = true;
      switch (node->info.name.meta_class)
        {
        case PT_META_CLASS:
        case PT_CLASSOID_ATTR:
          /* object is the class itself */
          obj = node->info.name.db_object;
          if (obj == NULL)
        {
          /* do nothing */
          return node;
        }
          is_class = db_is_any_class (obj);
          if (is_class < 0)
        {
          return node;
        }
          if (!is_class)
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
                  pt_short_print (parser, node));
          return node;
        }
          db_make_object (&db_value, obj);
          break;
        case PT_PARAMETER:
          pt_evaluate_tree_having_serial (parser, node, &db_value, 1);
          if (DB_VALUE_TYPE (&db_value) == DB_TYPE_VOBJ)
        {
          error = vid_vobj_to_object (&db_value, &obj);
          if (error != NO_ERROR)
            {
              PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
                  pt_short_print (parser, node));
            }
          db_make_object (&db_value, obj);
        }
          else if (DB_VALUE_TYPE (&db_value) == DB_TYPE_OBJECT)
        {
          obj = db_get_object (&db_value);
          obj = db_real_instance (obj);
          db_make_object (&db_value, obj);
        }
          break;
        default:
          /* do nothing here */
          return node;
        }
      /* replace node with value in db_value */
      result = pt_dbval_to_value (parser, &db_value);
      if (result == NULL)
        {
          if (!pt_has_error (parser))
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
                  pt_short_print (parser, node));
        }
          return node;
        }
      PT_NODE_MOVE_NUMBER_OUTERLINK (result, node);
      parser_free_tree (parser, node);
      return result;
    }
      /* look for node in attr list and stop when count reaches node_index */
      found = 0;
      val = eval->value_list;
      for (attr = eval->attr_list, count = 0; attr != NULL && count < eval->crt_attr_index; attr = attr->next, count++)
    {
      if (pt_name_equal (parser, node, attr))
        {
          found = true;
          break;
        }
      if (val != NULL)
        {
          val = val->next;
        }
    }
      /* replace found attributes with the corresponding values in value_list. if attribute is not found, it is
       * replaced with default value. if value_list is NULL, only default values are replaced */
      if (found && val != NULL)
    {
      /* replace node with value */
      if (!PT_IS_VALUE_NODE (val) && (!PT_IS_INSERT_VALUE_NODE (val) || !val->info.insert_value.is_evaluated))
        {
          /* cannot evaluate */
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME__CAN_NOT_EVALUATE,
              pt_short_print (parser, node));
          /* do not replace node */
          return node;
        }
      if (PT_IS_VALUE_NODE (val))
        {
          /* value node */
          result = parser_copy_tree (parser, val);
        }
      else
        {
          /* insert_value node */
          eval->reevaluate_needed = true;
          eval->replace_names = true;
          result = pt_dbval_to_value (parser, &val->info.insert_value.value);
        }
      if (result == NULL)
        {
          if (!pt_has_error (parser))
        {
          PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_OUT_OF_MEMORY, sizeof (PT_NODE));
        }
          /* do not replace node */
          return node;
        }
      /* replace node */
      PT_NODE_MOVE_NUMBER_OUTERLINK (result, node);
      parser_free_tree (parser, node);
      return result;
    }
      else if (!found)
    {
      /* try to replace node with default value */
      if (pt_resolve_default_value (parser, node) == NO_ERROR)
        {
          if (node->info.name.default_value != NULL)
        {
          /* replace node with default value */
          result = node->info.name.default_value;
          node->info.name.default_value = NULL;
        }
          else
        {
          /* replace with a NULL value */
          result = parser_new_node (parser, PT_VALUE);
          if (result == NULL)
            {
              if (!pt_has_error (parser))
            {
              PT_ERRORmf (parser, node, MSGCAT_SET_PARSER_RUNTIME, MSGCAT_RUNTIME_OUT_OF_MEMORY,
                      sizeof (PT_NODE));
            }
              return node;
            }
          result->type_enum = PT_TYPE_NULL;
        }
          PT_NODE_MOVE_NUMBER_OUTERLINK (result, node);
          parser_free_tree (parser, node);
          return result;
        }
    }
      /* could not assign a value, leave the node as it is */
      break;

    case PT_EXPR:
    case PT_FUNCTION:
      /* continue walk if current node is expression or function */
      *continue_walk = PT_CONTINUE_WALK;
      break;

      /* stop advancing in the tree if node is not a name, expression or function */

    case PT_VALUE:
      *continue_walk = PT_LIST_WALK;
      break;

    default:
      /* reevaluation may be needed for each execution */
      eval->reevaluate_needed = true;
      *continue_walk = PT_LIST_WALK;
      break;
    }

  return node;
}

/*
 * do_vacuum () - Executes a VACUUM statement.
 *
 * return     : Error code.
 * parser (in)    : Parser context.
 * statement (in) : VACUUM parse tree node.
 */
static int
do_vacuum (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;

  assert (parser != NULL && statement != NULL && PT_IS_VACUUM_NODE (statement));

  error = cvacuum ();

  return error;
}

/*
 * do_set_query_trace() - Set query trace
 *   return: NO_ERROR
 *   parser(in): Parser context
 *   statement(in): Parse tree of a set statement
 *
 */
int
do_set_query_trace (PARSER_CONTEXT * parser, PT_NODE * statement)
{
#if defined(SA_MODE)
  return NO_ERROR;
#else
  if (statement->info.trace.on_off == PT_TRACE_ON)
    {
      prm_set_bool_value (PRM_ID_QUERY_TRACE, true);

      if (statement->info.trace.format == PT_TRACE_FORMAT_TEXT)
    {
      prm_set_integer_value (PRM_ID_QUERY_TRACE_FORMAT, QUERY_TRACE_TEXT);
    }
      else if (statement->info.trace.format == PT_TRACE_FORMAT_JSON)
    {
      prm_set_integer_value (PRM_ID_QUERY_TRACE_FORMAT, QUERY_TRACE_JSON);
    }
    }
  else
    {
      prm_set_bool_value (PRM_ID_QUERY_TRACE, false);
    }

  return NO_ERROR;
#endif /* SA_MODE */
}

/*
 * do_kill() - Kill transaction or query
 *   return: Error code or number of killed transaction
 *   parser(in): Parser context
 *   statement(in): Parse tree of a set statement
 *
 */
int
do_kill (PARSER_CONTEXT * parser, PT_NODE * statement)
{
#if defined(SA_MODE)
  return NO_ERROR;
#else
  int error;
  PT_NODE *id_list;
  bool interrupt_only;
  int i = 0;
  int num_killed;
  int *tran_index_array;
  int array_size;
  bool is_dba_group_member;

  id_list = statement->info.killstmt.tran_id_list;
  array_size = pt_length_of_list (id_list);

  assert (array_size >= 1); /* verified in syntax check */

  is_dba_group_member = au_is_dba_group_member (Au_user);

  tran_index_array = (int *) malloc (sizeof (int) * array_size);
  if (tran_index_array == NULL)
    {
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, sizeof (int) * array_size);
      return error;
    }

  for (i = 0; id_list != NULL; id_list = id_list->next, i++)
    {
      assert (i < array_size);
      assert (id_list->type_enum == PT_TYPE_INTEGER);

      tran_index_array[i] = id_list->info.value.data_value.i;
    }

  interrupt_only = statement->info.killstmt.kill_type == KILLSTMT_QUERY ? true : false;

  error =
    thread_kill_or_interrupt_tran (tran_index_array, array_size, is_dba_group_member, interrupt_only, &num_killed);
  if (error == NO_ERROR)
    {
      error = num_killed;
    }

  free_and_init (tran_index_array);

  return error;
#endif
}

int
do_find_class_by_query (const char *name, char *buf, int buf_size)
{
#define QUERY_BUF_SIZE 2048
  DB_QUERY_RESULT *query_result = NULL;
  DB_QUERY_ERROR query_error;
  DB_VALUE value;
  const char *query = NULL;
  char query_buf[QUERY_BUF_SIZE] = { '\0' };
  const char *current_schema_name = NULL;
  const char *class_name = NULL;
  char qualifier_name[DB_MAX_USER_LENGTH] = { '\0' };
  int error = NO_ERROR;

  db_make_null (&value);
  query_error.err_lineno = 0;
  query_error.err_posno = 0;

  if (name == NULL || name[0] == '\0')
    {
      ERROR_SET_WARNING (error, ER_OBJ_INVALID_ARGUMENTS);
      return error;
    }

  assert (buf != NULL);

  current_schema_name = sc_current_schema_name ();

  if (sm_qualifier_name (name, qualifier_name, DB_MAX_USER_LENGTH) != NULL)
    {
      if (strcmp (qualifier_name, current_schema_name) != 0
      && strcmp (qualifier_name, Au_user_name) != 0 /* when AU_SET_USER() has been called */ )
    {
      /* Additional cross-schema object lookups during an ongoing cross-schema lookup
       * are beyond the scope of the compatibility option */
      assert (intl_identifier_casecmp (name, qualifier_name) != 0);
      ERROR_SET_WARNING_1ARG (error, ER_LC_UNKNOWN_CLASSNAME, name);
      return error;
    }
    }

  class_name = sm_remove_qualifier_name (name);
  query = "SELECT [unique_name] FROM [%s] WHERE [class_name] = '%s' AND [owner].[name] != UPPER ('%s')";
  assert (QUERY_BUF_SIZE > snprintf (NULL, 0, query, CT_CLASS_NAME, class_name, qualifier_name));
  snprintf (query_buf, QUERY_BUF_SIZE, query, CT_CLASS_NAME, class_name, qualifier_name);
  assert (query_buf[0] != '\0');

  error = db_compile_and_execute_local (query_buf, &query_result, &query_error);
  if (error < NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  error = db_query_first_tuple (query_result);
  if (error != DB_CURSOR_SUCCESS)
    {
      if (error == DB_CURSOR_END)
    {
      ERROR_SET_WARNING_1ARG (error, ER_LC_UNKNOWN_CLASSNAME, name);
    }
      else
    {
      ASSERT_ERROR ();
    }

      goto end;
    }

  error = db_query_get_tuple_value (query_result, 0, &value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  if (!DB_IS_NULL (&value))
    {
      assert (strlen (db_get_string (&value)) < buf_size);
      strncpy (buf, db_get_string (&value), buf_size);
    }
  else
    {
      /* unique_name must not be null. */
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

  error = db_query_next_tuple (query_result);
  if (error != DB_CURSOR_END)
    {
      /* No result can be returned because unique_name is not unique. */
      buf[0] = '\0';

      ERROR_SET_WARNING_1ARG (error, ER_LC_UNKNOWN_CLASSNAME, name);
    }

end:
  if (query_result)
    {
      db_query_end (query_result);
      query_result = NULL;
    }

  return error;
#undef QUERY_BUF_SIZE
}

int
do_find_serial_by_query (const char *name, char *buf, int buf_size)
{
#define QUERY_BUF_SIZE 2048
  DB_QUERY_RESULT *query_result = NULL;
  DB_QUERY_ERROR query_error;
  DB_VALUE value;
  const char *query = NULL;
  char query_buf[QUERY_BUF_SIZE] = { '\0' };
  const char *current_schema_name = NULL;
  const char *serial_name = NULL;
  char qualifier_name[DB_MAX_USER_LENGTH] = { '\0' };
  int error = NO_ERROR;

  db_make_null (&value);
  query_error.err_lineno = 0;
  query_error.err_posno = 0;

  if (name == NULL || name[0] == '\0')
    {
      ERROR_SET_WARNING (error, ER_OBJ_INVALID_ARGUMENTS);
      return error;
    }

  assert (buf != NULL);

  current_schema_name = sc_current_schema_name ();

  if (sm_qualifier_name (name, qualifier_name, DB_MAX_USER_LENGTH) != NULL)
    {
      if (strcmp (qualifier_name, current_schema_name) != 0)
    {
      /* Additional cross-schema object lookups during an ongoing cross-schema lookup
       * are beyond the scope of the compatibility option */
      assert (intl_identifier_casecmp (name, qualifier_name) != 0);
      return NO_ERROR;
    }
    }

  serial_name = sm_remove_qualifier_name (name);
  query = "SELECT [unique_name] FROM [%s] WHERE [name] = '%s' AND [owner].[name] != UPPER ('%s')";
  assert (QUERY_BUF_SIZE > snprintf (NULL, 0, query, CT_SERIAL_NAME, serial_name, qualifier_name));
  snprintf (query_buf, QUERY_BUF_SIZE, query, CT_SERIAL_NAME, serial_name, qualifier_name);
  assert (query_buf[0] != '\0');

  error = db_compile_and_execute_local (query_buf, &query_result, &query_error);
  if (error < NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  error = db_query_first_tuple (query_result);
  if (error != DB_CURSOR_SUCCESS)
    {
      if (error == DB_CURSOR_END)
    {
      /*
       * In the do_get_obj_id () function, if the MOP is NULL and the error is ER_OBJ_OBJECT_NOT_FOUND,
       * the error is cleared. So NO_ERROR is returned.
       *
       * In the do_create_auto_increment_serial() function, there should be no error even if the serial
       * cannot be found when the do_get_serial_obj_id() function is called. So it should return NO_ERROR.
       */
      error = NO_ERROR;
    }
      else
    {
      ASSERT_ERROR ();
    }

      goto end;
    }

  error = db_query_get_tuple_value (query_result, 0, &value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  if (!DB_IS_NULL (&value))
    {
      assert (strlen (db_get_string (&value)) < buf_size);
      strncpy (buf, db_get_string (&value), buf_size);
    }
  else
    {
      /* unique_name must not be null. */
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

  error = db_query_next_tuple (query_result);
  if (error != DB_CURSOR_END)
    {
      /* No result can be returned because unique_name is not unique. */
      buf[0] = '\0';
    }

end:
  if (query_result)
    {
      db_query_end (query_result);
      query_result = NULL;
    }

  return error;
#undef QUERY_BUF_SIZE
}

int
do_find_trigger_by_query (const char *name, char *buf, int buf_size)
{
#define QUERY_BUF_SIZE 2048
  DB_QUERY_RESULT *query_result = NULL;
  DB_QUERY_ERROR query_error;
  DB_VALUE value;
  const char *query = NULL;
  char query_buf[QUERY_BUF_SIZE] = { '\0' };
  const char *current_schema_name = NULL;
  const char *trigger_name = NULL;
  char qualifier_name[DB_MAX_USER_LENGTH] = { '\0' };
  int error = NO_ERROR;

  db_make_null (&value);
  query_error.err_lineno = 0;
  query_error.err_posno = 0;

  if (name == NULL || name[0] == '\0')
    {
      ERROR_SET_WARNING (error, ER_OBJ_INVALID_ARGUMENTS);
      return error;
    }

  assert (buf != NULL);

  current_schema_name = sc_current_schema_name ();

  if (sm_qualifier_name (name, qualifier_name, DB_MAX_USER_LENGTH) != NULL)
    {
      if (strcmp (qualifier_name, current_schema_name) != 0
      && strcmp (qualifier_name, Au_user_name) != 0 /* when AU_SET_USER() has been called */ )
    {
      /* Additional cross-schema object lookups during an ongoing cross-schema lookup
       * are beyond the scope of the compatibility option */
      assert (intl_identifier_casecmp (name, qualifier_name) != 0);
      return NO_ERROR;
    }
    }

  trigger_name = sm_remove_qualifier_name (name);
  query = "SELECT [unique_name] FROM [%s] WHERE [name] = '%s' AND [owner].[name] != UPPER ('%s')";
  assert (QUERY_BUF_SIZE > snprintf (NULL, 0, query, CT_TRIGGER_NAME, trigger_name, qualifier_name));
  snprintf (query_buf, QUERY_BUF_SIZE, query, CT_TRIGGER_NAME, trigger_name, qualifier_name);
  assert (query_buf[0] != '\0');

  error = db_compile_and_execute_local (query_buf, &query_result, &query_error);
  if (error < NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  error = db_query_first_tuple (query_result);
  if (error != DB_CURSOR_SUCCESS)
    {
      if (error == DB_CURSOR_END)
    {
      error = NO_ERROR;
    }
      else
    {
      ASSERT_ERROR ();
    }

      goto end;
    }

  error = db_query_get_tuple_value (query_result, 0, &value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  if (!DB_IS_NULL (&value))
    {
      assert (strlen (db_get_string (&value)) < buf_size);
      strncpy (buf, db_get_string (&value), buf_size);
    }
  else
    {
      /* unique_name must not be null. */
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

  error = db_query_next_tuple (query_result);
  if (error != DB_CURSOR_END)
    {
      /* No result can be returned because unique_name is not unique. */
      buf[0] = '\0';
    }

end:
  if (query_result)
    {
      db_query_end (query_result);
      query_result = NULL;
    }

  return error;
#undef QUERY_BUF_SIZE
}

int
do_find_synonym_by_query (const char *name, char *buf, int buf_size)
{
#define QUERY_BUF_SIZE 2048
  DB_QUERY_RESULT *query_result = NULL;
  DB_QUERY_ERROR query_error;
  DB_VALUE value;
  const char *query = NULL;
  char query_buf[QUERY_BUF_SIZE] = { '\0' };
  int error = NO_ERROR;

  db_make_null (&value);
  query_error.err_lineno = 0;
  query_error.err_posno = 0;

  if (name == NULL || name[0] == '\0')
    {
      ERROR_SET_WARNING (error, ER_OBJ_INVALID_ARGUMENTS);
      return error;
    }

  assert (buf != NULL);

  query = "SELECT [target_unique_name] FROM [%s] WHERE [unique_name] = '%s'";
  assert (QUERY_BUF_SIZE > snprintf (NULL, 0, query, CT_SYNONYM_NAME, name));
  snprintf (query_buf, QUERY_BUF_SIZE, query, CT_SYNONYM_NAME, name);

  error = db_compile_and_execute_local (query_buf, &query_result, &query_error);
  if (error < NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  error = db_query_first_tuple (query_result);
  if (error != DB_CURSOR_SUCCESS)
    {
      if (error == DB_CURSOR_END)
    {
      ERROR_SET_WARNING_1ARG (error, ER_SYNONYM_NOT_EXIST, name);
    }
      else
    {
      ASSERT_ERROR ();
    }

      goto end;
    }

  error = db_query_get_tuple_value (query_result, 0, &value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  if (!DB_IS_NULL (&value))
    {
      assert (strlen (db_get_string (&value)) < buf_size);
      strcpy (buf, db_get_string (&value));
    }
  else
    {
      /* unique_name must not be null. */
      assert (false);
    }

  error = db_query_next_tuple (query_result);
  if (error != DB_CURSOR_END)
    {
      /* No result can be returned because unique_name is not unique. */
      buf[0] = '\0';

      ERROR_SET_WARNING_1ARG (error, ER_SYNONYM_NOT_EXIST, name);
    }

end:
  if (query_result)
    {
      db_query_end (query_result);
      query_result = NULL;
    }

  return error;
#undef QUERY_BUF_SIZE
}

int
do_find_stored_procedure_by_query (const char *name, char *buf, int buf_size)
{
#define QUERY_BUF_SIZE 2048
  DB_QUERY_RESULT *query_result = NULL;
  DB_QUERY_ERROR query_error;
  DB_VALUE value;
  const char *query = NULL;
  char query_buf[QUERY_BUF_SIZE] = { '\0' };
  const char *current_schema_name = NULL;
  const char *sp_name = NULL;
  char qualifier_name[DB_MAX_USER_LENGTH] = { '\0' };
  int error = NO_ERROR;

  db_make_null (&value);
  query_error.err_lineno = 0;
  query_error.err_posno = 0;

  if (name == NULL || name[0] == '\0')
    {
      ERROR_SET_WARNING (error, ER_OBJ_INVALID_ARGUMENTS);
      return error;
    }

  assert (buf != NULL);

  current_schema_name = sc_current_schema_name ();

  if (sm_qualifier_name (name, qualifier_name, DB_MAX_USER_LENGTH) != NULL)
    {
      if (strcmp (qualifier_name, current_schema_name) != 0)
    {
      /* Additional cross-schema object lookups during an ongoing cross-schema lookup
       * are beyond the scope of the compatibility option */
      assert (intl_identifier_casecmp (name, qualifier_name) != 0);
      ERROR_SET_WARNING_1ARG (error, ER_SP_NOT_EXIST, name);
      return error;
    }
    }

  sp_name = sm_remove_qualifier_name (name);
  query = "SELECT [unique_name] FROM [%s] WHERE [sp_name] = '%s' AND [owner].[name] != UPPER ('%s')";
  assert (QUERY_BUF_SIZE > snprintf (NULL, 0, query, CT_STORED_PROC_NAME, sp_name, qualifier_name));
  snprintf (query_buf, QUERY_BUF_SIZE, query, CT_STORED_PROC_NAME, sp_name, qualifier_name);
  assert (query_buf[0] != '\0');

  error = db_compile_and_execute_local (query_buf, &query_result, &query_error);
  if (error < NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  error = db_query_first_tuple (query_result);
  if (error != DB_CURSOR_SUCCESS)
    {
      if (error == DB_CURSOR_END)
    {
      ERROR_SET_WARNING_1ARG (error, ER_SP_NOT_EXIST, name);
    }
      else
    {
      ASSERT_ERROR ();
    }

      goto end;
    }

  error = db_query_get_tuple_value (query_result, 0, &value);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto end;
    }

  if (!DB_IS_NULL (&value))
    {
      assert (strlen (db_get_string (&value)) < buf_size);
      strcpy (buf, db_get_string (&value));
    }
  else
    {
      /* unique_name must not be null. */
      ASSERT_ERROR_AND_SET (error);
      goto end;
    }

  error = db_query_next_tuple (query_result);
  if (error != DB_CURSOR_END)
    {
      /* No result can be returned because unique_name is not unique. */
      buf[0] = '\0';

      ERROR_SET_WARNING_1ARG (error, ER_SP_NOT_EXIST, name);
    }

end:
  if (query_result)
    {
      db_query_end (query_result);
      query_result = NULL;
    }

  return error;
#undef QUERY_BUF_SIZE
}

/*
 * do_set_trace_to_query_flag() -
 *   return: void
 *   query_flag(in):
 */
static void
do_set_trace_to_query_flag (QUERY_FLAG * query_flag)
{
  int trace_format;

  trace_format = prm_get_integer_value (PRM_ID_QUERY_TRACE_FORMAT);

  if (trace_format == QUERY_TRACE_TEXT)
    {
      *query_flag |= XASL_TRACE_TEXT;
    }
  else if (trace_format == QUERY_TRACE_JSON)
    {
      *query_flag |= XASL_TRACE_JSON;
    }
}

/*
 * do_send_plan_trace_to_session() - send plan dump to server session
 *   return: void
 *   parser(in):
 */
static void
do_send_plan_trace_to_session (PARSER_CONTEXT * parser)
{
  DB_VALUE var[2];
  char *plan_str = NULL;
  int i = 0;
  QUERY_TRACE_FORMAT format;
  size_t sizeloc;
  FILE *fp;

  if (parser->num_plan_trace < 1)
    {
      return;
    }

  format = parser->plan_trace[0].format;

  if (format == QUERY_TRACE_TEXT)
    {
      if (parser->num_plan_trace > 1)
    {
      fp = port_open_memstream (&plan_str, &sizeloc);

      if (fp)
        {
          for (i = 0; i < parser->num_plan_trace; i++)
        {
          assert (parser->plan_trace[i].format == format);
          fprintf (fp, "%s\n", parser->plan_trace[i].trace.text_plan);
          free_and_init (parser->plan_trace[i].trace.text_plan);
        }

          port_close_memstream (fp, &plan_str, &sizeloc);
        }
    }
      else
    {
      plan_str = parser->plan_trace[0].trace.text_plan;
      parser->plan_trace[0].trace.text_plan = NULL;
    }
    }
  else if (format == QUERY_TRACE_JSON)
    {
      json_t *jplan;

      if (parser->num_plan_trace > 1)
    {
      jplan = json_array ();

      for (i = 0; i < parser->num_plan_trace; i++)
        {
          assert (parser->plan_trace[i].format == format);
          json_array_append_new (jplan, parser->plan_trace[i].trace.json_plan);
          parser->plan_trace[i].trace.json_plan = NULL;
        }
    }
      else
    {
      jplan = parser->plan_trace[0].trace.json_plan;
      parser->plan_trace[0].trace.json_plan = NULL;
    }

      plan_str = json_dumps (jplan, JSON_INDENT (2) | JSON_PRESERVE_ORDER);

      json_object_clear (jplan);
      json_decref (jplan);
    }

  parser->num_plan_trace = 0;

  if (plan_str != NULL)
    {
      db_make_char (&var[0], 10, "trace_plan", 10, LANG_COERCIBLE_CODESET, LANG_COERCIBLE_COLL);
      db_make_string (&var[1], plan_str);

      csession_set_session_variables (var, 2);
      free_and_init (plan_str);
    }
}

/*
 * do_insert_checks ()    : - Does preliminary checks for an insert statement.
 *
 * return         : NO_ERROR or error code.
 * parser(in)         : Parser context.
 * statement(in)      : Parse tree of the insert statement to be checked.
 * class_(in)         :
 * update(in/out)     : Update attribute.
 * values(in)         : Values to be inserted.
 */

static int
do_insert_checks (PARSER_CONTEXT * parser, PT_NODE * statement, PT_NODE ** class_, PT_NODE ** update, PT_NODE * values)
{
  int error = NO_ERROR;
  int upd_has_uniques = 0;
  bool has_default_values_list = false;
  int trigger_involved = 0;
  *update = NULL;

  /* Check if server allows an insert. */
  error = is_server_insert_allowed (parser, statement);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto exit;
    }

  /* Check whether the statement can be executed with commit. */
  if (statement->info.insert.server_allowed == SERVER_INSERT_IS_ALLOWED)
    {
      /* Check statement insert trigger. */
      error = sm_class_has_triggers ((*class_)->info.name.db_object, &trigger_involved, TR_EVENT_STATEMENT_INSERT);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto exit;
    }

      if (!trigger_involved && !(statement->info.insert.hint & PT_HINT_USE_SBR))
    {
      statement->info.insert.execute_with_commit_allowed = 1;
    }
    }

  /* Check non null attrs. */
  if (values->info.node_list.list_type == PT_IS_DEFAULT_VALUE)
    {
      has_default_values_list = true;
    }

  error = check_missing_non_null_attrs (parser, statement->info.insert.spec, statement->info.insert.attr_list,
                    has_default_values_list);
  if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto exit;
    }

  /* Test if server UPDATE is allowed */

  if (statement->info.insert.odku_assignments != NULL)
    {
      *update = do_create_odku_stmt (parser, statement);
      if (*update == NULL)
    {
      error = ER_FAILED;
      goto exit;
    }

      if (statement->info.insert.server_allowed == SERVER_INSERT_IS_ALLOWED)
    {
      int server_allowed = 0;

      error = is_server_update_allowed (parser, &statement->info.insert.odku_non_null_attrs, &upd_has_uniques,
                        &server_allowed, *update);
      if (error != NO_ERROR)
        {
          ASSERT_ERROR ();
          goto exit;
        }

      if (!server_allowed)
        {
          statement->info.insert.server_allowed = SERVER_INSERT_IS_NOT_ALLOWED;
          goto exit;
        }
    }
    }

  /* Check to see if the class into which we are inserting is part of an inheritance chain. We do not allow these
   * statements to be executed in these cases as we might have undefined behavior, such as trying to update a
   * column that belongs to a child for a duplicate key in the parent table that does not have that column. */

  if (statement->info.insert.do_replace || statement->info.insert.odku_assignments != NULL)
    {
      int allowed = 0;

      error = is_replace_or_odku_allowed ((*class_)->info.name.db_object, &allowed);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      goto exit;
    }

      if (!allowed)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_REPLACE_ODKU_NOT_ALLOWED, 0);
      ASSERT_ERROR_AND_SET (error);
      goto exit;
    }
    }

  /* fetch the class for instance write purpose - IX_LOCK */
  if (!locator_fetch_class ((*class_)->info.name.db_object, DB_FETCH_CLREAD_INSTWRITE))
    {
      ASSERT_ERROR_AND_SET (error);
      goto exit;
    }

exit:
  return error;
}

#define SERVER_ATTR_LINK_NAME   "link_name"
#define SERVER_ATTR_HOST        "host"
#define SERVER_ATTR_PORT        "port"
#define SERVER_ATTR_DB_NAME     "db_name"
#define SERVER_ATTR_USER_NAME   "user_name"
#define SERVER_ATTR_PROPERTIES  "properties"
#define SERVER_ATTR_COMMENT     "comment"
#define SERVER_ATTR_PASSWORD    "password"
#define SERVER_ATTR_OWNER       "owner"
#define SERVER_ATTR_LINK_NAME_BUF_SIZE  (255)   // link_name varchar(255)

static MOP
do_get_server_obj_id (DB_IDENTIFIER * server_obj_id, DB_OBJECT * server_class_mop, const char *server_name)
{
  return do_get_obj_id (server_obj_id, server_class_mop, server_name, SERVER_ATTR_LINK_NAME);
}

int
do_drop_server (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  DB_OBJECT *server_object = NULL;
  PT_DROP_SERVER_INFO *drop_server;
  int error;

  CHECK_MODIFICATION_ERROR ();

  drop_server = &(statement->info.drop_server);
  server_object = server_find (drop_server->server_name, drop_server->owner_name);
  if (server_object == NULL)
    {
      error = er_errid ();
      if (drop_server->if_exists && (error == ER_DBLINK_SERVER_NOT_FOUND))
    {
      error = NO_ERROR;
    }
      return error;
    }

  int save;
  AU_DISABLE (save);
  error = db_drop (server_object);
  AU_ENABLE (save);
  return error;
}


static int
do_create_server_internal (MOP * server_object, DB_VALUE * port_no, DB_VALUE * passwd, MOP owner,
               const char **attr_names, char **attr_val, int attr_cnt)
{
  DB_OBJECT *ret_obj = NULL;
  DB_OTMPL *obj_tmpl = NULL;
  DB_VALUE value;
  DB_OBJECT *server_class = NULL;
  int au_save, error = NO_ERROR;

  db_make_null (&value);

  /* temporarily disable authorization to access _db_server class */
  AU_DISABLE (au_save);

  server_class = sm_find_class (CT_SERVER_NAME);
  if (server_class == NULL)
    {
      error = ER_DBLINK_CATALOG_DB_SERVER_NOT_FOUND;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
      goto end;
    }

  obj_tmpl = dbt_create_object_internal ((MOP) server_class);
  if (obj_tmpl == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }

  /* port */
  error = dbt_put_internal (obj_tmpl, SERVER_ATTR_PORT, port_no);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* password */
  error = dbt_put_internal (obj_tmpl, SERVER_ATTR_PASSWORD, passwd);
  if (error != NO_ERROR)
    {
      goto end;
    }

  for (int i = 0; i < attr_cnt; i++)
    {
      if (attr_val[i] == NULL)
    {
      db_make_null (&value);
    }
      else
    {
      db_make_string (&value, attr_val[i]);
    }

      error = dbt_put_internal (obj_tmpl, attr_names[i], &value);
      pr_clear_value (&value);
      if (error != NO_ERROR)
    {
      goto end;
    }
    }

  /* owner */
  db_make_object (&value, owner);
  error = dbt_put_internal (obj_tmpl, SERVER_ATTR_OWNER, &value);
  pr_clear_value (&value);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* created_time && updated_time */
  error = db_set_otmpl_timestamps (obj_tmpl);
  if (error != NO_ERROR)
    {
      goto end;
    }

  ret_obj = dbt_finish_object (obj_tmpl);
  if (ret_obj == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
    }
  else if (server_object != NULL)
    {
      *server_object = ret_obj;
    }

end:
  if (obj_tmpl != NULL && ret_obj == NULL)
    {
      dbt_abort_object (obj_tmpl);
    }
  AU_ENABLE (au_save);
  return error;
}

int
do_create_server (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  DB_OBJECT *server_object = NULL;
  DB_VALUE *pval = NULL;
  DB_VALUE port_no, passwd;
  DB_DATA_STATUS data_stat;
  char *pwd;
  char *attr_val[6];
  const char *attr_names[6] = { SERVER_ATTR_LINK_NAME, SERVER_ATTR_HOST, SERVER_ATTR_DB_NAME,
    SERVER_ATTR_USER_NAME, SERVER_ATTR_PROPERTIES, SERVER_ATTR_COMMENT
  };
  MOP owner_obj = Au_user;
  char name_buf[SERVER_ATTR_LINK_NAME_BUF_SIZE + 1];    // link_name varchar(255)
  int error = NO_ERROR;
  int save;

  PT_CREATE_SERVER_INFO *create_server = &statement->info.create_server;

  CHECK_MODIFICATION_ERROR ();

  memset (attr_val, 0x00, sizeof (attr_val));
  db_make_null (&passwd);
  db_make_int (&port_no, 0);

  if (create_server->owner_name)
    {
      owner_obj = db_find_user ((char *) create_server->owner_name->info.name.original);
      if (owner_obj == NULL)
    {
      assert (er_errid () != NO_ERROR);
      if (ER_IS_SERVER_DOWN_ERROR (er_errid ()))
        {
          error = ER_NET_CANT_CONNECT_SERVER;
        }
      else
        {
          error = ER_AU_INVALID_USER;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_AU_INVALID_USER, 1,
              create_server->owner_name->info.name.original);
        }
      return error;
    }
    }

  sm_downcase_name ((char *) create_server->server_name->info.name.original, name_buf, SERVER_ATTR_LINK_NAME_BUF_SIZE);
  attr_val[0] = name_buf;
  server_object = server_find (create_server->server_name, create_server->owner_name);
  if (server_object != NULL)
    {
      error = ER_DBLINK_SERVER_ALREADY_EXISTS;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, attr_val[0]);
      goto end;
    }

  error = er_errid ();
  if (error != ER_DBLINK_SERVER_NOT_FOUND)
    {
      if (error == ER_DBLINK_SERVER_MULTIPLE_FOUND)
    {
      error = ER_DBLINK_SERVER_ALREADY_EXISTS;
    }
      goto end;
    }

  /* HOST */
  assert (create_server->host->node_type == PT_VALUE);
  attr_val[1] = (char *) PT_VALUE_GET_BYTES (create_server->host);
  if (attr_val[1] == NULL)
    {
      error = ER_FAILED;
      goto end;
    }

  /* PORT */
  db_value_domain_init (&port_no, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);
  pval = pt_value_to_db (parser, create_server->port);
  if (pval == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }

  error = numeric_db_value_coerce_to_num (pval, &port_no, &data_stat);
  if (error != NO_ERROR)
    {
      goto end;
    }
  pval = NULL;

  /* DBNAME */
  assert (create_server->dbname->node_type == PT_NAME || create_server->dbname->node_type == PT_VALUE);
  // *INDENT-OFF* 
  attr_val[2] = (create_server->dbname->node_type == PT_NAME) ? (char *) create_server->dbname->info.name.original
                                                              : (char *) PT_VALUE_GET_BYTES (create_server->dbname);
  // *INDENT-ON* 
  if (attr_val[2] == NULL)
    {
      error = ER_FAILED;
      goto end;
    }

  /* USER */
  assert (create_server->user->node_type == PT_NAME || create_server->user->node_type == PT_VALUE);
  // *INDENT-OFF* 
  attr_val[3] = (create_server->user->node_type == PT_NAME) ? (char *) create_server->user->info.name.original
                                                            : (char *) PT_VALUE_GET_BYTES (create_server->user);
  // *INDENT-ON* 
  if (attr_val[3] == NULL)
    {
      error = ER_FAILED;
      goto end;
    }

  /* PASSWORD */
  assert (create_server->pwd);
  assert (create_server->pwd->node_type == PT_VALUE);
  pwd = (char *) PT_VALUE_GET_BYTES (create_server->pwd);
  if (pwd == NULL || *pwd == '\0')
    {
      error = ER_FAILED;
      goto end;
    }

  error = db_make_string_copy (&passwd, pwd);
  if (error != NO_ERROR)
    {
      goto end;
    }

  /* PROPERTIES */
  if (create_server->prop != NULL)
    {
      assert (create_server->prop->node_type == PT_VALUE);
      attr_val[4] = (char *) PT_VALUE_GET_BYTES (create_server->prop);
      if (attr_val[4] == NULL)
    {
      error = ER_FAILED;
      goto end;
    }
    }

  /* COMMENT */
  if (create_server->comment != NULL)
    {
      assert (create_server->comment->node_type == PT_VALUE);
      attr_val[5] = (char *) PT_VALUE_GET_BYTES (create_server->comment);
      if (attr_val[5] == NULL)
    {
      error = ER_FAILED;
      goto end;
    }
    }

  server_object = NULL;
  /* now create server object which is insert into _db_server */
  AU_DISABLE (save);
  error =
    do_create_server_internal (&server_object, &port_no, &passwd, owner_obj, attr_names, attr_val,
                   sizeof (attr_names) / sizeof (attr_names[0]));
  AU_ENABLE (save);
  if (error >= 0)
    {
      error = NO_ERROR;
    }

end:
  pr_clear_value (&port_no);
  pr_clear_value (&passwd);

  return error;
}

int
do_rename_server (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  DB_OBJECT *server_object = NULL;
  char name_buf[SERVER_ATTR_LINK_NAME_BUF_SIZE + 1];
  DB_VALUE value;
  int save;
  PT_RENAME_SERVER_INFO *rename_server = &(statement->info.rename_server);

  CHECK_MODIFICATION_ERROR ();

  server_object = server_find (rename_server->old_name, rename_server->owner_name);
  if (server_object == NULL)
    {
      return er_errid ();
    }


  // If rename_server->owner_name is not specified, the owner information of the existing server must be maintained.
  // e.g; If u1.srv and u1.test exist and "rename server test as srv" is performed in the dba account, 
  //      it is necessary to check whether "u1.srv" exists. It is not to check with "dba.srv" or "srv"
  PT_NODE *owner_node = rename_server->owner_name;
  if (rename_server->owner_name == NULL)
    {
      DB_VALUE owner_val, name_val;

      AU_DISABLE (save);
      error = db_get (server_object, SERVER_ATTR_OWNER, &owner_val);
      if (error == NO_ERROR)
    {
      error = db_get (db_get_object (&owner_val), "name", &name_val);
    }
      AU_ENABLE (save);

      if (error != NO_ERROR)
    {
      pr_clear_value (&owner_val);
      pr_clear_value (&name_val);
      return error;
    }

      owner_node = parser_new_node (parser, PT_NAME);
      if (owner_node == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
      return MSGCAT_RUNTIME_OUT_OF_MEMORY;
    }

      owner_node->info.name.original = pt_append_string (parser, NULL, db_get_string (&name_val));
      pr_clear_value (&owner_val);
      pr_clear_value (&name_val);
    }

  if (server_find (rename_server->new_name, owner_node))
    {
      error = ER_DBLINK_SERVER_ALREADY_EXISTS;
    }
  else
    {
      error = er_errid ();
      if (error == ER_DBLINK_SERVER_MULTIPLE_FOUND)
    {
      error = ER_DBLINK_SERVER_ALREADY_EXISTS;
    }
    }

  if (owner_node && owner_node != rename_server->owner_name)
    {
      parser_free_node (parser, owner_node);
    }

  const char *new_name = rename_server->new_name->info.name.original;
  if (error != ER_DBLINK_SERVER_NOT_FOUND)
    {
      if (error == ER_DBLINK_SERVER_ALREADY_EXISTS)
    {
      if (rename_server->owner_name)
        {
          char err_buf[2048];
          sprintf (err_buf, "[%s].[%s]", (char *) rename_server->owner_name->info.name.original, new_name);
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, err_buf);
        }
      else
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, new_name);
        }
    }
      return error;
    }

  er_clear ();

  sm_downcase_name (new_name, name_buf, SERVER_ATTR_LINK_NAME_BUF_SIZE);
  db_make_string (&value, name_buf);

  AU_DISABLE (save);
  error = db_put (server_object, SERVER_ATTR_LINK_NAME, &value);
  AU_ENABLE (save);

  pr_clear_value (&value);

  if (error == NO_ERROR)
    {
      AU_DISABLE (save);
      error = db_update_obj_timestamp (server_object);
      AU_ENABLE (save);
    }

  return error;
}

int
do_alter_server (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  int error = NO_ERROR;
  char *pt;
  const char *server_name;
  DB_OBJECT *server_object = NULL;
  DB_VALUE value, passwd;
  PT_ALTER_SERVER_INFO *alter;
  int save;

  CHECK_MODIFICATION_ERROR ();

  db_make_null (&value);
  alter = &(statement->info.alter_server);
  server_name = alter->server_name->info.name.original;

  server_object = server_find (alter->server_name, alter->current_owner_name);
  if (server_object == NULL)
    {
      return er_errid ();
    }

  AU_DISABLE (save);

  if (alter->xbits.bit_pwd)
    {
      assert (alter->pwd && alter->pwd->node_type == PT_VALUE);
      pt = (char *) PT_VALUE_GET_BYTES (alter->pwd);
      assert (pt && *pt);

      db_make_null (&passwd);
      error = db_make_string_copy (&passwd, pt);
      if (error != NO_ERROR)
    {
      goto end;
    }
      error = db_put (server_object, SERVER_ATTR_PASSWORD, &passwd);
      pr_clear_value (&passwd);
      if (error != NO_ERROR)
    {
      goto end;
    }
    }

  if (alter->xbits.bit_host)
    {
      assert (alter->host->node_type == PT_VALUE);
      pt = (char *) PT_VALUE_GET_BYTES (alter->host);
      assert (pt && *pt);

      db_make_string (&value, pt);
      error = db_put (server_object, SERVER_ATTR_HOST, &value);
      pr_clear_value (&value);
      if (error != NO_ERROR)
    {
      goto end;
    }
    }

  if (alter->xbits.bit_port)
    {
      DB_VALUE *pval = NULL;
      DB_DATA_STATUS data_stat;

      db_value_domain_init (&value, DB_TYPE_NUMERIC, DB_MAX_NUMERIC_PRECISION, 0);
      pval = pt_value_to_db (parser, alter->port);
      if (pval == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      goto end;
    }

      error = numeric_db_value_coerce_to_num (pval, &value, &data_stat);
      if (error != NO_ERROR)
    {
      goto end;
    }
      pval = NULL;

      error = db_put (server_object, SERVER_ATTR_PORT, &value);
      pr_clear_value (&value);
      if (error != NO_ERROR)
    {
      goto end;
    }
    }

  if (alter->xbits.bit_dbname)
    {
      assert (alter->dbname->node_type == PT_NAME || alter->dbname->node_type == PT_VALUE);
      if (alter->dbname->node_type == PT_VALUE)
    {
      pt = (char *) PT_VALUE_GET_BYTES (alter->dbname);
    }
      else
    {
      pt = (char *) alter->dbname->info.name.original;
    }

      assert (pt && *pt);
      db_make_string (&value, pt);
      error = db_put (server_object, SERVER_ATTR_DB_NAME, &value);
      pr_clear_value (&value);
      if (error != NO_ERROR)
    {
      goto end;
    }
    }

  if (alter->xbits.bit_user)
    {
      assert (alter->user->node_type == PT_NAME || alter->user->node_type == PT_VALUE);
      if (alter->user->node_type == PT_VALUE)
    {
      pt = (char *) PT_VALUE_GET_BYTES (alter->user);
    }
      else
    {
      pt = (char *) alter->user->info.name.original;
    }

      assert (pt && *pt);
      db_make_string (&value, pt);
      error = db_put (server_object, SERVER_ATTR_USER_NAME, &value);
      pr_clear_value (&value);
      if (error != NO_ERROR)
    {
      goto end;
    }
    }

  if (alter->xbits.bit_prop)
    {
      if (alter->prop)
    {
      assert (alter->prop->node_type == PT_VALUE);
      pt = (char *) PT_VALUE_GET_BYTES (alter->prop);
    }
      else
    {
      pt = 0x00;
    }

      if (!pt)
    {
      db_make_null (&value);
    }
      else
    {
      db_make_string (&value, pt);
    }
      error = db_put (server_object, SERVER_ATTR_PROPERTIES, &value);
      pr_clear_value (&value);
      if (error != NO_ERROR)
    {
      goto end;
    }
    }

  if (alter->xbits.bit_comment)
    {
      if (alter->comment)
    {
      assert (alter->comment->node_type == PT_VALUE);
      pt = (char *) PT_VALUE_GET_BYTES (alter->comment);
    }
      else
    {
      pt = 0x00;
    }

      if (!pt)
    {
      db_make_null (&value);
    }
      else
    {
      db_make_string (&value, pt);
    }

      error = db_put (server_object, SERVER_ATTR_COMMENT, &value);
      pr_clear_value (&value);
      if (error != NO_ERROR)
    {
      goto end;
    }
    }

  if (alter->xbits.bit_owner)
    {
      assert (alter->owner_name->node_type == PT_NAME);
      pt = (char *) alter->owner_name->info.name.original;
      assert (pt && *pt);

      MOP user = db_find_user (pt);
      if (user == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (ER_IS_SERVER_DOWN_ERROR (error))
        {
          error = ER_NET_CANT_CONNECT_SERVER;
        }
      goto end;
    }

      if (server_find (alter->server_name, alter->owner_name))
    {
      char buf[2048];
      sprintf (buf, "[%s].[%s]",
           (char *) alter->owner_name->info.name.original, (char *) alter->server_name->info.name.original);
      error = ER_DBLINK_SERVER_ALREADY_EXISTS;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, buf);
      goto end;
    }

      error = er_errid ();
      if (error != ER_DBLINK_SERVER_NOT_FOUND)
    {
      if (error == ER_DBLINK_SERVER_MULTIPLE_FOUND)
        {
          error = ER_DBLINK_SERVER_ALREADY_EXISTS;
        }
      goto end;
    }

      db_make_object (&value, user);
      error = db_put (server_object, SERVER_ATTR_OWNER, &value);
      pr_clear_value (&value);
      if (error != NO_ERROR)
    {
      goto end;
    }
    }

  error = db_update_obj_timestamp (server_object);
  if (error != NO_ERROR)
    {
      goto end;
    }

end:
  AU_ENABLE (save);

  return error;
}

int
get_dblink_info_from_dbserver (PARSER_CONTEXT * parser, PT_NODE * server_name, PT_NODE * owner_name, DB_VALUE * out_val)
{
  DB_OBJECT *server_object = NULL;
  DB_VALUE values[4], pwd_val;
  int au_save, error, cnt;
  const char *url_attr_names[4] = { SERVER_ATTR_HOST, SERVER_ATTR_PORT, SERVER_ATTR_DB_NAME, SERVER_ATTR_PROPERTIES };
  MOP user_obj = NULL;
  DB_VALUE user_val;

  db_make_null (&user_val);

  cnt = 0;
  server_object = server_find (server_name, owner_name);
  if (server_object == NULL)
    {
      return er_errid ();
    }

  AU_DISABLE (au_save);     // disable checking authorization
  for (cnt = 0; cnt < 4; cnt++)
    {
      db_make_null (&(values[cnt]));
      error = db_get (server_object, url_attr_names[cnt], &(values[cnt]));
      if (error < 0)
    {
      goto error_end;
    }
    }

  error = db_get (server_object, SERVER_ATTR_USER_NAME, &(out_val[1]));
  if (error < 0)
    {
      goto error_end;
    }

  error = db_get (server_object, SERVER_ATTR_PASSWORD, &pwd_val);
  if (error < 0)
    {
      goto error_end;
    }

  if (owner_name == NULL)
    {
      error = db_get (server_object, SERVER_ATTR_OWNER, &user_val);
      if (error < 0)
    {
      goto error_end;
    }

      user_obj = db_get_object (&user_val);
      error = db_get (user_obj, "name", &(out_val[3]));
      if (error < 0)
    {
      goto error_end;
    }
    }

  error = get_dblink_password_decrypt (db_get_string (&pwd_val), &(out_val[2]));
  if (error != NO_ERROR)
    {
      if (error == ER_DBLINK_PASSWORD_CHECKSUM || error == ER_DBLINK_PASSWORD_INVALID_LENGTH)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 0);
    }
      else
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DBLINK_PASSWORD_DECRYPT, 1, error);
      error = ER_DBLINK_PASSWORD_DECRYPT;
    }
    }
  else
    {
      // cci:CUBRID:<host>:<port>:<db_name>:<db_user>:<db_password>:[?<properties>]
      const char *dblink_url_fmt_prop = "cci:CUBRID:%s:%d:%s:::?%s";
      const char *dblink_url_fmt_none = "cci:CUBRID:%s:%d:%s:::";
      char dblink_url[4096];
      int port_no;
      char *host, *dbname, *prop;

      host = (char *) db_get_string (&(values[0]));
      port_no = db_get_int (&(values[1]));
      dbname = (char *) db_get_string (&(values[2]));
      prop = (char *) db_get_string (&(values[3]));

      if (prop && *prop)
    {
      sprintf (dblink_url, dblink_url_fmt_prop, host, port_no, dbname, ((prop[0] == '?') ? prop + 1 : prop));
    }
      else
    {
      sprintf (dblink_url, dblink_url_fmt_none, host, port_no, dbname);
    }

      error = db_make_string_copy (&(out_val[0]), dblink_url);
    }

error_end:
  AU_ENABLE (au_save);

  pr_clear_value (&pwd_val);
  while (--cnt >= 0)
    {
      pr_clear_value (&(values[cnt]));
    }
  pr_clear_value (&user_val);

  return error;
}

int
get_dblink_owner_name_from_dbserver (PARSER_CONTEXT * parser, PT_NODE * server_nm, PT_NODE * owner_nm,
                     DB_VALUE * out_val)
{
  DB_OBJECT *server_object = NULL;
  MOP user_obj = NULL;
  int au_save, error = NO_ERROR;
  DB_VALUE user_val;

  db_make_null (&user_val);

  server_object = server_find (server_nm, owner_nm);
  if (server_object == NULL)
    {
      return er_errid ();
    }

  AU_DISABLE (au_save);     // disable checking authorization

  if (owner_nm == NULL)
    {
      error = db_get (server_object, SERVER_ATTR_OWNER, &user_val);
      if (error >= 0)
    {
      user_obj = db_get_object (&user_val);
      error = db_get (user_obj, "name", out_val);
    }
    }

  AU_ENABLE (au_save);
  pr_clear_value (&user_val);

  return error;
}

#define DBLINK_PASSWORD_MAX_LENGTH      (128)
#define DBLINK_PASSWORD_CONFUSED_LENGTH (6) // include 4(int) + 1(unsigned char) +  1(unsigned char)
// Valid data size is the largest multiple of 3 less than or equal to DBLINK_PASSWORD_CIPHER_LENGTH.
#define DBLINK_PASSWORD_CIPHER_LENGTH   (DBLINK_PASSWORD_MAX_LENGTH + DBLINK_PASSWORD_CONFUSED_LENGTH)
#define DBLINK_PASSWORD_PAD_LENGTH      (40)    // include 2(length) + 2(mk) + 2(length) + 32(mk), Must be 4 or more
#define DBLINK_PASSWORD_MAX_BUFSIZE  ((int)(DBLINK_PASSWORD_CIPHER_LENGTH / 3 * 4) + DBLINK_PASSWORD_PAD_LENGTH)

/*
 * pt_check_dblink_password ()  : Check the validity of the entered password.
 *
 * return         : NO_ERROR or error code.
 * parser(in)         : Parser context.
 * passwd(in)             : Password entered by user(or parser)
 * cipher_buf(out)    : Encrypted password
 * ciper_buf_size(in)     : Size of cipher_buf
 * 
 * Remark: 
 *     Checks whether the entered password is a raw password or an encrypted password,
 *     if it is a raw password, encrypt it.
 *     The length of the raw password is shorter than DBLINK_PASSWORD_MAX_LENGTH.
 *     If it is an encrypted password, it must have a specific length.
 *     Even if there is no raw password, it will be returned as an encrypted password.
 */
int
pt_check_dblink_password (PARSER_CONTEXT * parser, const char *passwd, char *cipher_buf, int ciper_buf_size)
{
  DB_VALUE val;
  char *str;
  int max_len = DBLINK_PASSWORD_MAX_BUFSIZE;
  int err = ER_FAILED;
  int length = 0;

  /* Adjust the length so that it is a multiple of 4. */
  max_len >>= 2;
  max_len <<= 2;

  db_make_null (&val);

  if (ciper_buf_size <= max_len)
    {
      err = ER_TF_BUFFER_OVERFLOW;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err, 0);
      goto ret_pos;
    }

  if (passwd && *passwd)
    {
      length = strlen (passwd);
    }

  if (length <= DBLINK_PASSWORD_MAX_LENGTH)
    {
      // The raw password entered by the user.  
      err = get_dblink_password_encrypt (passwd, &val);
      if (err == NO_ERROR)
    {
      str = (char *) db_get_string (&val);
      if (!str)
        {
          err = ER_FAILED;
          goto ret_pos;
        }
      else
        {
          strcpy (cipher_buf, str);
        }
    }
      else
    {
      if (err == ER_DBLINK_PASSWORD_OVER_MAX_LENGTH)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err, 0);
        }
      else
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DBLINK_PASSWORD_ENCRYPT, 1, err);
          err = ER_DBLINK_PASSWORD_ENCRYPT;
        }
    }
    }
  else if (length == max_len)
    {
      err = get_dblink_password_decrypt (passwd, &val);
      if (err == NO_ERROR)
    {
      // A encrypted password from the raw password.      
      strcpy (cipher_buf, passwd);
    }
      else
    {
      if (err == ER_DBLINK_PASSWORD_CHECKSUM || err == ER_DBLINK_PASSWORD_INVALID_LENGTH)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err, 0);
        }
      else
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DBLINK_PASSWORD_DECRYPT, 1, err);
          err = ER_DBLINK_PASSWORD_DECRYPT;
        }
    }
    }

ret_pos:
  pr_clear_value (&val);
  if (err != NO_ERROR)
    {
      if (er_errid_if_has_error () != NO_ERROR)
    {
      PT_ERROR (parser, pt_top (parser), (char *) er_msg ());
    }
      else if (!pt_has_error (parser))
    {
      PT_ERROR (parser, pt_top (parser), "Failed to check PASSWORD.");
    }
    }

  return err;
}

/*
 * get_dblink_password_encrypt ()  : Generates an encrypted password.
 *
 * return         : NO_ERROR or error code.
 * passwd(in)             : Raw password
 * encrypt_val(out)   : Encrypted password
 * 
 * Remark: 
 *      
 */
static int
get_dblink_password_encrypt (const char *passwd, DB_VALUE * encrypt_val)
{
  int err, length, buf_size;
  char cipher[DBLINK_PASSWORD_CIPHER_LENGTH + 1], newpwd[DBLINK_PASSWORD_MAX_BUFSIZE + 1];
  char confused[DBLINK_PASSWORD_CIPHER_LENGTH + 1] = { 0, };
  unsigned char private_key[DBLINK_CRYPT_KEY_LENGTH] = { 0, };  // Do NOT omit this initialize.
  struct timeval check_time = { 0, 0 };
  struct tm *lt;
  char empty_str[4] = { 0x00, };

  srand (time (NULL));

  db_make_null (encrypt_val);
  if (!passwd)
    {
      passwd = empty_str;
    }

  if (strlen (passwd) > DBLINK_PASSWORD_MAX_LENGTH)
    {
      return ER_DBLINK_PASSWORD_OVER_MAX_LENGTH;
    }

  length = shake_dblink_password (passwd, confused, DBLINK_PASSWORD_CIPHER_LENGTH, &check_time);
  passwd = confused;

  if ((lt = localtime ((time_t *) & check_time.tv_sec)) == NULL)
    {
      sprintf ((char *) private_key, "%08ld%06ld", check_time.tv_sec, check_time.tv_usec);
    }
  else
    {
      if (snprintf ((char *) private_key, sizeof (private_key), "%04d%02d%02d%02d%02d%02d%06ld",
            lt->tm_year + 1900, lt->tm_mon + 1, lt->tm_mday, lt->tm_hour, lt->tm_min, lt->tm_sec,
            check_time.tv_usec) >= (int) sizeof (private_key))
    {
      assert_release (0);
      private_key[sizeof (private_key) - 1] = '\0';
    }
    }

  err = crypt_dblink_encrypt ((unsigned char *) passwd, length, (unsigned char *) cipher, private_key);
  if (err == NO_ERROR)
    {
      err =
    crypt_dblink_bin_to_str (cipher, length, newpwd, DBLINK_PASSWORD_MAX_BUFSIZE, private_key,
                 (long) check_time.tv_usec);
      if (err == NO_ERROR)
    {
      // byte stream to hex string   
      err = db_make_string_copy (encrypt_val, newpwd);
    }
    }

  return err;
}

/*
 * get_dblink_password_decrypt ()  : Decrypt the encrypted password to extract the raw password.
 *
 * return         : NO_ERROR or error code.
 * parser(in)         : Parser context.
 * passwd_cipher(in)      : Encrypted password
 * decrypt_val(out)   : Raw password
 * 
 * Remark: 
 *      
 */
static int
get_dblink_password_decrypt (const char *passwd_cipher, DB_VALUE * decrypt_val)
{
  int err, length, new_length;
  char cipher[DBLINK_PASSWORD_CIPHER_LENGTH + 1], newpwd[DBLINK_PASSWORD_CIPHER_LENGTH + 1];
  unsigned char private_key[DBLINK_CRYPT_KEY_LENGTH] = { 0, };  // Do NOT omit this initialize.

  db_make_null (decrypt_val);
  if (!passwd_cipher || !*passwd_cipher)
    {
      return NO_ERROR;
    }

  new_length = DBLINK_PASSWORD_MAX_BUFSIZE;
  /* Adjust the length so that it is a multiple of 4. */
  new_length >>= 2;
  new_length <<= 2;

  length = strlen (passwd_cipher);
  if (length != new_length)
    {
      return ER_DBLINK_PASSWORD_INVALID_LENGTH;
    }

  // hex string  to byte stream 
  err = crypt_dblink_str_to_bin (passwd_cipher, length, cipher, &new_length, private_key);
  if (err != NO_ERROR)
    {
      return ER_DBLINK_PASSWORD_INVALID_FMT;
    }

  err = crypt_dblink_decrypt ((unsigned char *) cipher, new_length, (unsigned char *) newpwd, private_key);
  if (err == NO_ERROR)
    {
      newpwd[new_length] = '\0';    // Do NOT omit this line.
      err = reverse_shake_dblink_password (newpwd, new_length, cipher);
      if (err != NO_ERROR)
    {
      return ER_DBLINK_PASSWORD_CHECKSUM;
    }
      err = db_make_string_copy (decrypt_val, cipher);
    }

  return err;
}

/*
 * server_find ()  : Query by server name in the _db_server catalog.
 *
 * return         : Record object or NULL
 * node_server(in)    : PT_NODE* for server name.
 * node_owner(in)         : PT_NODE* for owner name. 
 * 
 * Remark: 
 *      
 */
static MOP
server_find (PT_NODE * node_server, PT_NODE * node_owner)
{
  int error = NO_ERROR;
  MOP server_obj = NULL;
  char *upper_case_name = NULL;
  size_t name_size;
  char *owner_name = NULL;
  char query[2048];
  char server_name_lwr[SERVER_ATTR_LINK_NAME_BUF_SIZE + 1]; // link_name varchar(255)

  sm_downcase_name ((char *) node_server->info.name.original, server_name_lwr, SERVER_ATTR_LINK_NAME_BUF_SIZE);
  if (node_owner)
    {
      owner_name = (char *) node_owner->info.name.original;
      name_size = intl_identifier_upper_string_size (owner_name);
      upper_case_name = (char *) malloc (name_size + 1);
      if (upper_case_name == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, (name_size + 1));
      return NULL;
    }
      intl_identifier_upper (owner_name, upper_case_name);
      owner_name = upper_case_name;
    }
  else
    {
      owner_name = (char *) au_get_current_user_name ();
      if (!owner_name)
    {
      return NULL;
    }
    }

  assert (owner_name);
  sprintf (query,
       "SELECT [_db_server], [owner] FROM [_db_server] WHERE [link_name] = '%s' AND [owner].[name] = '%s'",
       server_name_lwr, owner_name);

  DB_QUERY_RESULT *query_result;
  DB_QUERY_ERROR query_error;
  DB_VALUE values[2];
  int au_save;
  int rec_cnt = 0;
  int saved_opt_level;

  PARSER_CONTEXT *parser = parser_create_parser ();
  if (parser == NULL)
    {
      // return NULL;
      goto clear_and_return;
    }

  db_make_null (&values[0]);
  db_make_null (&values[1]);

  AU_DISABLE (au_save);

  /*
   * backup the optimization level for executing internal query to find server-name,
   * because it could not be executed depending on the optimization level.
   */
  saved_opt_level = prm_get_integer_value (PRM_ID_OPTIMIZATION_LEVEL);
  prm_set_integer_value (PRM_ID_OPTIMIZATION_LEVEL, 1);

  error = db_compile_and_execute_local (query, &query_result, &query_error);

  prm_set_integer_value (PRM_ID_OPTIMIZATION_LEVEL, saved_opt_level);

  if (error < 0)
    {
      goto err;
    }
  else if (error == 0)
    {
      error = ER_DBLINK_SERVER_NOT_FOUND;
    }
  else
    {
      error = db_query_first_tuple (query_result);
      if (error != DB_CURSOR_SUCCESS)
    {
      goto err;
    }

      do
    {
      error = db_query_get_tuple_value (query_result, 1, &values[1]);
      if (error != NO_ERROR)
        {
          goto err;
        }
      /* check if user is creator or DBA  */
      if (au_is_server_authorized_user (&values[1]))
        {
          rec_cnt++;
          error = db_query_get_tuple_value (query_result, 0, &values[0]);
          if (error != NO_ERROR)
        {
          goto err;
        }
          server_obj = db_get_object (&values[0]);
        }

      db_value_clear (&values[0]);
      db_value_clear (&values[1]);
      if (rec_cnt > 1)
        break;
    }
      while (db_query_next_tuple (query_result) == DB_CURSOR_SUCCESS);
      if (rec_cnt == 0)
    {
      error = ER_DBLINK_SERVER_ALTER_NOT_ALLOWED;   // ER_DBLINK_CANNOT_UPDATE_SERVER
    }
    }

  if (rec_cnt != 1)
    {
      char owner_name_lwr[DB_MAX_USER_LENGTH + 1];
      server_obj = NULL;

      sm_downcase_name (owner_name, owner_name_lwr, sizeof (owner_name_lwr));
      sprintf (query, "[%s].[%s]", owner_name_lwr, server_name_lwr);

      if (rec_cnt == 0)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, error, 1, query);
    }
      else
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DBLINK_SERVER_MULTIPLE_FOUND, 1, query);
    }
    }
  error = NO_ERROR;

err:
  parser_free_parser (parser);
  db_query_end (query_result);

  AU_ENABLE (au_save);

  if (error != NO_ERROR)
    {
      db_value_clear (&values[0]);
      db_value_clear (&values[1]);
    }

clear_and_return:
  if (owner_name == upper_case_name)
    {
      free (upper_case_name);
    }
  else
    {
      db_string_free ((char *) owner_name);
    }

  return server_obj;
}

bool
pt_is_allowed_result_cache ()
{
  int is_list_cache_disabled =
    ((prm_get_integer_value (PRM_ID_LIST_MAX_QUERY_CACHE_ENTRIES) <= 0)
     || (prm_get_integer_value (PRM_ID_LIST_MAX_QUERY_CACHE_PAGES) <= 0));

  if (is_list_cache_disabled)
    {
      return false;
    }

  return true;
}