Skip to content

File db_vdb.c

File List > compat > db_vdb.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.
 *
 */

/*
 * db_vdb.c - Stubs for SQL interface functions.
 */

#ident "$Id$"

#include "config.h"

#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/timeb.h>
#include <time.h>
#include <assert.h>

#include "authenticate.h"
#include "db.h"
#include "dbi.h"
#include "db_query.h"
#include "error_manager.h"
#include "chartype.h"
#include "system_parameter.h"
#include "environment_variable.h"
#include "memory_alloc.h"
#include "parser.h"
#include "parser_message.h"
#include "object_domain.h"
#include "object_primitive.h"
#include "schema_manager.h"
#include "view_transform.h"
#include "execute_statement.h"
#include "locator_cl.h"
#include "server_interface.h"
#include "db_session.h"
#include "network_interface_cl.h"
#include "transaction_cl.h"
#include "dbtype.h"
#include "util_func.h"
#include "xasl.h"
#include "query_cl.h"

#define BUF_SIZE 1024

#define MAX_SERVER_TIME_CACHE   60  /* secs */

enum
{
  StatementInitialStage = 0,
  StatementCompiledStage,
  StatementPreparedStage,
  StatementExecutedStage,
};

static struct timeb base_server_timeb = { 0, 0, 0, 0 };
static struct timeb base_client_timeb = { 0, 0, 0, 0 };

static int get_dimension_of (PT_NODE ** array);
static DB_SESSION *db_open_local (void);
static DB_SESSION *initialize_session (DB_SESSION * session);
static int db_execute_and_keep_statement_local (DB_SESSION * session, int stmt_ndx, DB_QUERY_RESULT ** result);
static DB_OBJLIST *db_get_all_chosen_classes (int (*p) (MOBJ o));
static int is_vclass_object (MOBJ class_);
static char *get_reasonable_predicate (DB_ATTRIBUTE * att, char *predicate, int predicate_buf_sz);
static void update_execution_values (PARSER_CONTEXT * parser, int result, CUBRID_STMT_TYPE statement_type);
static void copy_execution_values (EXECUTION_STATE_VALUES * source, EXECUTION_STATE_VALUES * destination);
static int values_list_to_values_array (PARSER_CONTEXT * parser, PT_NODE * values_list, DB_VALUE_ARRAY * values_array);
static int set_prepare_info_into_list (DB_PREPARE_INFO * prepare_info, PT_NODE * statement);
static PT_NODE *char_array_to_name_list (PARSER_CONTEXT * parser, char **names, int length);
static int do_process_prepare_statement (DB_SESSION * session, PT_NODE * statement);
static int do_get_prepared_statement_info (DB_SESSION * session, int stmt_idx, int *subquery_num,
                       DB_PREPARE_SUBQUERY_INFO ** subquery_info);
static int do_set_user_host_variables (DB_SESSION * session, PT_NODE * using_list);
static int do_cast_host_variables_to_expected_domain (DB_SESSION * session);
static int do_recompile_and_execute_prepared_statement (DB_SESSION * session, PT_NODE * statement,
                            DB_QUERY_RESULT ** result);
static int do_process_deallocate_prepare (DB_SESSION * session, PT_NODE * statement);
static bool is_allowed_as_prepared_statement (PT_NODE * node);
static bool is_allowed_as_prepared_statement_with_hv (PT_NODE * node);
static bool db_check_limit_need_recompile (PARSER_CONTEXT * parser, PT_NODE * statement, int xasl_flag);
static bool db_check_where_need_recompile (PARSER_CONTEXT * parser, PT_NODE * statement, int xasl_flag);

static DB_CLASS_MODIFICATION_STATUS pt_has_modified_class (PARSER_CONTEXT * parser, PT_NODE * statement);
static PT_NODE *pt_has_modified_class_helper (PARSER_CONTEXT * parser, PT_NODE * tree, void *arg, int *continue_walk);
static bool db_can_execute_statement_with_autocommit (PARSER_CONTEXT * parser, PT_NODE * statement);
static PT_NODE *do_process_prepare_subquery_pre (PARSER_CONTEXT * parser, PT_NODE * stmt, void *arg,
                         int *continue_walk);

static PT_NODE *do_execute_cte_pre (PARSER_CONTEXT * parser, PT_NODE * stmt, void *arg, int *continue_walk);

int g_open_buffer_control_flags = 0;

/*
 * get_dimemsion_of() - returns the number of elements of a null-terminated
 *   pointer array
 * returns  : number of elements of array
 * array (in): a null-terminated array of pointers
 */
static int
get_dimension_of (PT_NODE ** array)
{
  int rank = 0;

  if (!array)
    {
      return rank;
    }

  while (*array++)
    {
      rank++;
    }

  return rank;
}

/*
 * db_statement_count() - This function returns the number of statements
 *    in a session.
 * return : number of statements in the session
 * session(in): compiled session
 */
int
db_statement_count (DB_SESSION * session)
{
  int retval;

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

  retval = get_dimension_of (session->statements);

  return (retval);
}

/*
 * db_open_local() - Starts a new SQL empty compile session
 * returns : new DB_SESSION
 */
static DB_SESSION *
db_open_local (void)
{
  DB_SESSION *session = NULL;

  session = (DB_SESSION *) malloc (sizeof (DB_SESSION));
  if (session == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (DB_SESSION));
      return NULL;
    }

  session->parser = parser_create_parser ();
  if (session->parser == NULL)
    {
      free_and_init (session);
      return NULL;
    }

  assert (session->parser->query_id == NULL_QUERY_ID);

  session->stage = NULL;
  session->dimension = 0;
  session->stmt_ndx = 0;
  session->type_list = NULL;
  session->line_offset = 0;
  session->include_oid = DB_NO_OIDS;
  session->statements = NULL;
  session->is_subsession_for_prepared = false;
  session->next = NULL;

  return session;
}

/*
 * initialize_session() -
 * returns  : DB_SESSION *, NULL if fails
 * session(in/out):
 */
static DB_SESSION *
initialize_session (DB_SESSION * session)
{
  assert (session != NULL && session->statements != NULL);

  session->dimension = get_dimension_of (session->statements);

  return session;
}

/*
 * db_open_buffer_local() - Please refer to the db_open_buffer() function
 * returns  : new DB_SESSION
 * buffer(in): contains query text to be compiled
 */
DB_SESSION *
db_open_buffer_local (const char *buffer)
{
  DB_SESSION *session;

  CHECK_1ARG_NULL (buffer);

  session = db_open_local ();

  if (session)
    {
      if (g_open_buffer_control_flags & PARSER_FOR_PLCSQL_STATIC_SQL)
    {
      session->parser->flag.is_parsing_static_sql = 1;
    }

      session->statements = parser_parse_string_with_escapes (session->parser, buffer, false);
      if (session->statements)
    {
      return initialize_session (session);
    }
    }

  return session;
}

/*
 * db_open_buffer() - Starts a new SQL compile session on a nul terminated
 *    string
 * return:new DB_SESSION
 * buffer(in) : contains query text to be compiled
 */
DB_SESSION *
db_open_buffer (const char *buffer)
{
  DB_SESSION *session;

  CHECK_1ARG_NULL (buffer);
  CHECK_CONNECT_NULL ();

  session = db_open_buffer_local (buffer);

  return session;
}


/*
 * db_open_file() - Starts a new SQL compile session on a query file
 * returns  : new DB_SESSION
 * file(in): contains query text to be compiled
 */
DB_SESSION *
db_open_file (FILE * file)
{
  DB_SESSION *session;

  CHECK_CONNECT_NULL ();

  session = db_open_local ();
  if (session)
    {
      session->statements = parser_parse_file (session->parser, file);
      if (session->statements)
    {
      return initialize_session (session);
    }
    }

  return session;
}

void
db_init_lexer_lineno ()
{
  csql_yyset_lineno (1);
}

/*
 * db_make_session_for_one_statement_execution() -
 * return:
 * file(in) :
 */
DB_SESSION *
db_make_session_for_one_statement_execution (FILE * file)
{
  DB_SESSION *session;

  CHECK_CONNECT_NULL ();

  session = db_open_local ();
  if (session)
    {
      pt_init_one_statement_parser (session->parser, file);
      parse_one_statement (0);
    }

  return session;
}

/*
 * db_parse_one_statement() -
 * return:
 * session(in) :
 */
int
db_parse_one_statement (DB_SESSION * session)
{
  if (session->dimension > 0)
    {
      /* check if this statement is skipped */
      if (session->type_list)
    {
      db_free_query_format (session->type_list[0]);
      session->type_list[0] = NULL;
    }
      if (session->statements[0])
    {
      parser_free_tree (session->parser, session->statements[0]);
      session->statements[0] = NULL;
    }

      session->dimension = 0;
      session->stmt_ndx = 0;

      session->parser->stack_top = 0;
      if (session->stage)
    {
      session->stage[0] = StatementInitialStage;
    }
    }

  if (parse_one_statement (1) == 0 && !pt_has_error (session->parser) && session->parser->stack_top > 0
      && session->parser->node_stack != NULL)
    {
      session->parser->statements = (PT_NODE **) parser_alloc (session->parser, 2 * sizeof (PT_NODE *));
      if (session->parser->statements == NULL)
    {
      return -1;
    }

      session->parser->statements[0] = session->parser->node_stack[0];
      session->parser->statements[1] = NULL;

      session->statements = session->parser->statements;
      session->dimension = get_dimension_of (session->statements);

      return session->dimension;
    }
  else
    {
      session->parser->statements = NULL;
      return -1;
    }
}

/*
 * db_get_parser_line_col() -
 * return:
 * session(in) :
 * line(out) :
 * col(out) :
 */
int
db_get_parser_line_col (DB_SESSION * session, int *line, int *col)
{
  if (line)
    {
      *line = session->parser->line;
    }
  if (col)
    {
      *col = session->parser->column;
    }
  return 0;
}

/*
 * db_open_file_name() - This functions allocates and initializes a session and
 *    parses the named file. Similar to db_open_file() except that it takes a
 *    name rather than a file handle.
 * return : new session
 * name(in): file name
 */
DB_SESSION *
db_open_file_name (const char *name)
{
  FILE *fp;
  DB_SESSION *session;

  CHECK_CONNECT_NULL ();

  session = db_open_local ();
  if (session)
    {
      fp = fopen (name, "r");
      if (fp != NULL)
    {
      session->statements = parser_parse_file (session->parser, fp);
      fclose (fp);
    }
      if (session->statements)
    {
      return initialize_session (session);
    }
    }

  return session;
}

static void
db_calculate_current_time (struct timeb *tb)
{
  assert (tb != nullptr);

  // use chrono functions to populate timeb
  time_t sec;
  int millisec;
  util_get_second_and_ms_since_epoch (&sec, &millisec);
  tb->time = sec;
  tb->millitm = (unsigned short) millisec;
}

/*
 * db_calculate_current_server_time () -
 * return:
 * parser(in) :
 * server_info(in) :
 */
static void
db_calculate_current_server_time (PARSER_CONTEXT * parser)
{

  struct tm *c_time_struct;
  DB_DATETIME datetime;

  struct timeb curr_server_timeb;
  struct timeb curr_client_timeb;
  int diff_mtime;
  int diff_time;

  if (base_server_timeb.time == 0)
    {
      return;
    }

  db_calculate_current_time (&curr_client_timeb);
  diff_time = (int) (curr_client_timeb.time - base_client_timeb.time);
  diff_mtime = curr_client_timeb.millitm - base_client_timeb.millitm;

  if (diff_time > MAX_SERVER_TIME_CACHE)
    {
      base_server_timeb.time = 0;
    }
  else
    {
      curr_server_timeb.time = base_server_timeb.time;
      curr_server_timeb.millitm = base_server_timeb.millitm;

      /* timeb.millitm is unsigned short, so should prevent underflow */
      if (diff_mtime < 0)
    {
      curr_server_timeb.time--;
      curr_server_timeb.millitm += 1000;
    }

      curr_server_timeb.time += diff_time;
      curr_server_timeb.millitm += diff_mtime;

      if (curr_server_timeb.millitm >= 1000)
    {
      curr_server_timeb.time++;
      curr_server_timeb.millitm -= 1000;
    }

      c_time_struct = localtime (&curr_server_timeb.time);
      if (c_time_struct == NULL)
    {
      base_server_timeb.time = 0;
    }
      else
    {
      db_datetime_encode (&datetime, c_time_struct->tm_mon + 1, c_time_struct->tm_mday,
                  c_time_struct->tm_year + 1900, c_time_struct->tm_hour, c_time_struct->tm_min,
                  c_time_struct->tm_sec, curr_server_timeb.millitm);

      db_make_datetime (&parser->sys_datetime, &datetime);
      db_make_timestamp (&parser->sys_epochtime, (DB_TIMESTAMP) curr_server_timeb.time);
    }
    }
}

/*
 * db_set_base_server_time() -
 * return:
 * server_info(in) :
 */
static void
db_set_base_server_time (DB_VALUE * db_val)
{
  struct tm c_time_struct;
  DB_TIME time_val;
  DB_DATETIME *dt = db_get_datetime (db_val);

  time_val = dt->time / 1000;   /* milliseconds to seconds */
  db_tm_encode (&c_time_struct, &dt->date, &time_val);

  base_server_timeb.millitm = dt->time % 1000;  /* set milliseconds */

  base_server_timeb.time = mktime (&c_time_struct);
  db_calculate_current_time (&base_client_timeb);
}

/*
 * db_compile_statement_local() -
 * return:
 * session(in) :
 */
int
db_compile_statement_local (DB_SESSION * session)
{
  PARSER_CONTEXT *parser;
  int stmt_ndx;
  PT_NODE *statement = NULL;
  PT_NODE *statement_result = NULL;
  DB_QUERY_TYPE *qtype, *q;
  CUBRID_STMT_TYPE cmd_type;
  int err;
  static long seed = 0;

  /* obvious error checking - invalid parameter */
  if (!session || !session->parser)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_INVALID_SESSION, 0);
      return er_errid ();
    }
  /* no statement was given in the session */
  if (session->dimension == 0 || !session->statements)
    {
      /* if the parser already has something wrong - syntax error */
      if (pt_has_error (session->parser))
    {
      pt_report_to_ersys (session->parser, PT_SYNTAX);
      return er_errid ();
    }

      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_EMPTY_STATEMENT, 0);
      return er_errid ();
    }
  /* check the end of statements */
  if (session->stmt_ndx == session->dimension)
    {
      /* return 0 if all statement were compiled */
      return 0;
    }

  /* allocate memory for session->type_list and session->stage if not allocated */
  if (session->type_list == NULL)
    {
      size_t size = session->dimension * sizeof (DB_QUERY_TYPE *) + session->dimension * sizeof (char);
      void *p = malloc (size);
      if (p == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, size);
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }
      (void) memset (p, 0, size);
      session->type_list = (DB_QUERY_TYPE **) p;
      session->stage = (char *) p + session->dimension * sizeof (DB_QUERY_TYPE *);
    }

  /*
   * Compilation Stage
   */

  /* the statements in this session have been parsed without error and it is time to compile the next statement */
  parser = session->parser;
  stmt_ndx = session->stmt_ndx++;
  statement = session->statements[stmt_ndx];
  statement->flag.use_plan_cache = 0;
  statement->flag.use_query_cache = 0;

  statement->flag.is_system_generated_stmt = parser->flag.is_system_generated_stmt;

  /* check if the statement is already processed */
  if (session->stage[stmt_ndx] >= StatementPreparedStage)
    {
      return stmt_ndx + 1;
    }

  /* forget about any previous parsing errors, if any */
  pt_reset_error (parser);

  /*
     pt_rewrite_for_dblink:
     the dblink with remote-table-name@server-name should be converted as
     1) pure remote-DML query like UPDATE remote-table-name SET ...
     2) SELECT ... FROM DBLink (server-name, 'SELECT ... ')
   */
  pt_rewrite_for_dblink (parser, statement);
  if (pt_has_error (parser))
    {
      pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, statement);
      return er_errid ();
    }

  /* store user-specified-name in info.name.original. */
  parser_walk_tree (parser, statement, NULL, NULL, pt_set_user_specified_name, NULL);

  /* get type list describing the output columns titles of the given query */
  cmd_type = pt_node_to_cmd_type (statement);
  qtype = NULL;
  if (cmd_type == CUBRID_STMT_SELECT)
    {
      qtype = pt_get_titles (parser, statement);
      /* to prevent a memory leak, register the query type list to session */
      session->type_list[stmt_ndx] = qtype;
    }
  else if (cmd_type == CUBRID_STMT_EXECUTE_PREPARE)
    {
      /* we don't actually have the statement which will be executed and we need to get some information about it from
       * the server */
      DB_PREPARE_SUBQUERY_INFO *subquery_info;
      int i, num_query;
      int err = do_get_prepared_statement_info (session, stmt_ndx, &num_query, &subquery_info);

      QUERY_ID query_id;
      QFILE_LIST_ID *list_id;

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

      /* execute subqueries first */
      if (subquery_info)
    {
      int q;

      err = do_execute_prepared_subquery (parser, statement, num_query, subquery_info);

      /* clear subquery's prepare info. */
      for (q = 0; q < num_query; q++)
        {
          if (subquery_info[q].host_var_index)
        {
          free (subquery_info[q].host_var_index);
        }
        }

      free (subquery_info);

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

  /* prefetch and lock classes to avoid deadlock */
  (void) pt_class_pre_fetch (parser, statement);
  if (pt_has_error (parser))
    {
      pt_report_to_ersys_with_statement (parser, PT_SYNTAX, statement);
      return er_errid ();
    }

  if (seed == 0)
    {
      srand48 (seed = (long) time (NULL));
    }

  /* do semantic check for the statement */
  statement_result = pt_compile (parser, statement);

  if (statement_result == NULL || pt_has_error (parser))
    {
      pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, statement);
      return er_errid ();
    }
  statement = statement_result;

  /* get type list describing the output columns titles of the given query */
  if (cmd_type == CUBRID_STMT_SELECT)
    {
      /* for a select-type query of the form: SELECT * FROM class c we store into type_list the nice column headers:
       * c.attr1 attr2 c.attr3 before they get fully resolved by mq_translate(). */
      if (!qtype)
    {
      qtype = pt_get_titles (parser, statement);
      /* to prevent a memory leak, register the query type list to session */
      session->type_list[stmt_ndx] = qtype;
    }
      if (qtype)
    {
      /* NOTE, this is here on purpose. If something is busting because it tries to continue corresponding this
       * type information and the list file columns after having jacked with the list file, by for example adding a
       * hidden OID column, fix the something else. This needs to give the results as user views the query, ie
       * related to the original text. It may guess wrong about attribute/column updatability. Thats what they
       * asked for. */
      qtype = pt_fillin_type_size (parser, statement, qtype, DB_NO_OIDS, false, false);

      /* check implicit oid included for excluding result cache */
      for (q = qtype; q; q = q->next)
        {
          if (q->col_type == DB_COL_PATH)
        {
          statement->info.query.flag.do_not_cache = 1;
          break;
        }
        }
    }
    }

  /*
     the remote-DML of dblink must not execute mq_translate
     because the query of remote-DML should be executed at remote server side
   */
  if (PT_IS_DBLINK_DML_QUERY (statement))
    {
      /*
         The remote DML query can not use the cached plan,
         because the atuo-variable holder can not be composed during compiling.
         Instead the remote query is generated by parser_print_tree.
       */
      statement->flag.recompile = 1;
    }
  else
    {
      /* translate views or virtual classes into base classes */
      statement_result = mq_translate (parser, statement);
      if (!statement_result || pt_has_error (parser))
    {
      pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, statement);
      return er_errid ();
    }
      statement = statement_result;

      /* prefetch and lock translated real classes to avoid deadlock */
      (void) pt_class_pre_fetch (parser, statement);
      if (pt_has_error (parser))
    {
      pt_report_to_ersys_with_statement (parser, PT_SYNTAX, statement);
      return er_errid ();
    }

      /* validate include_oid setting in the session */
      if (session->include_oid != DB_NO_OIDS)
    {
      if (mq_updatable (parser, statement) == PT_UPDATABLE)
        {
          if (session->include_oid == DB_ROW_OIDS)
        {
          (void) pt_add_row_oid (parser, statement);
        }
        }
      else
        {
          /* disallow OID column for non-updatable query */
          session->include_oid = DB_NO_OIDS;
        }
    }
    }

  /* so now, the statement is compiled */
  session->statements[stmt_ndx] = statement;
  session->stage[stmt_ndx] = StatementCompiledStage;


  /*
   * Preparation Stage
   */

  statement->xasl_id = NULL;    /* bullet proofing */

  /* New interface of do_prepare_statement()/do_execute_statment() is used only when the XASL cache is enabled. If it
   * is disabled, old interface of do_statement() will be used instead. do_statement() makes a XASL everytime rather
   * than using XASL cache. Also, it can be executed in the server without touching the XASL cache by calling
   * prepare_and_execute_query(). */
  if (!parser->flag.is_parsing_static_sql && prm_get_integer_value (PRM_ID_XASL_CACHE_MAX_ENTRIES) > 0
      && statement->flag.cannot_prepare == 0)
    {
      if (session->is_subsession_for_prepared)
    {
      /* cast host variables to their expected domain, before XASL generation. */
      session->parser->flag.set_host_var = 0;
      err = do_cast_host_variables_to_expected_domain (session);
      if (err < 0)
        {
          if (pt_has_error (parser))
        {
          pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, statement);
          return er_errid ();
        }
          return err;
        }
    }

      /* now, prepare the statement by calling do_prepare_statement() */
      err = do_prepare_statement (parser, statement);
#if 0
      if (err == ER_QPROC_INVALID_XASLNODE)
    {
      /* There is a kind of problem in the XASL cache. It is possible when the cache entry was deleted by the
       * other. In this case, retry to prepare once more (generate and stored the XASL again). */
      statement->xasl_id = NULL;
      er_clear ();
      /* execute the statement by calling do_statement() */
      err = do_prepare_statement (parser, statement);
    }
#endif
      if (err < 0)
    {
      if (pt_has_error (parser))
        {
          pt_report_to_ersys_with_statement (parser, PT_SEMANTIC, statement);
          return er_errid ();
        }
      return err;
    }
    }

  /* so now, the statement is prepared */
  session->stage[stmt_ndx] = StatementPreparedStage;

  return stmt_ndx + 1;
}

/*
 * db_compile_statement() - This function compiles the next statement in the
 *    session. The first compilation reports any syntax errors that occurred
 *    in the entire session.
 * return: an integer that is the relative statement ID of the next statement
 *    within the session, with the first statement being statement number 1.
 *    If there are no more statements in the session (end of statements), 0 is
 *    returned. If an error occurs, the return code is negative.
 * session(in) : session handle
 */
int
db_compile_statement (DB_SESSION * session)
{
  int statement_id;

  er_clear ();

  CHECK_CONNECT_MINUSONE ();

  statement_id = db_compile_statement_local (session);

  return statement_id;
}

/*
 * db_rewind_statement() -
 * return:
 * session(in) :
 */
void
db_rewind_statement (DB_SESSION * session)
{
  if (session->dimension == session->stmt_ndx)
    {
      session->stmt_ndx = 1;
    }
}

/*
 * db_session_is_last_statement() -
 * return:
 * session(in) :
 */
int
db_session_is_last_statement (DB_SESSION * session)
{
  assert (session->dimension > 0);
  return session->dimension == session->stmt_ndx;
}

/*
 * db_set_client_cache_time() -
 * return:
 * session(in) :
 * stmt_ndx(in) :
 * cache_time(in) :
 */
int
db_set_client_cache_time (DB_SESSION * session, int stmt_ndx, CACHE_TIME * cache_time)
{
  PT_NODE *statement;
  int result = NO_ERROR;

  if (!session || !session->parser || session->dimension == 0 || !session->statements || stmt_ndx < 1
      || stmt_ndx > session->dimension || !(statement = session->statements[stmt_ndx - 1]))
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      result = er_errid ();
    }
  else
    {
      if (cache_time)
    {
      statement->cache_time = *cache_time;
      statement->flag.clt_cache_check = 1;
    }
    }

  return result;
}

/*
 * db_get_jdbccachehint() -
 * return:
 * session(in) :
 * stmt_ndx(in) :
 * life_time(out) :
 */
bool
db_get_jdbccachehint (DB_SESSION * session, int stmt_ndx, int *life_time)
{
  PT_NODE *statement;

  /* obvious error checking - invalid parameter */
  if (!session || !session->parser || session->dimension == 0 || !session->statements || stmt_ndx < 1
      || stmt_ndx > session->dimension || !(statement = session->statements[stmt_ndx - 1]))
    {
      return false;
    }

  if (statement->info.query.q.select.hint & PT_HINT_JDBC_CACHE)
    {
      if (statement->info.query.q.select.jdbc_life_time)
    {
      if (life_time != NULL && statement->info.query.q.select.jdbc_life_time->info.name.original != NULL)
        {
          *life_time = atoi (statement->info.query.q.select.jdbc_life_time->info.name.original);
        }
      return true;
    }
    }

  return false;
}

/*
 * db_get_useplancache() -
 * return:
 * session(in) :
 * stmt_ndx(in) :
 * life_time(out) :
 */
bool
db_get_cacheinfo (DB_SESSION * session, int stmt_ndx, bool * use_plan_cache, bool * use_query_cache)
{
  PT_NODE *statement;

  /* obvious error checking - invalid parameter */
  if (!session || !session->parser || session->dimension == 0 || !session->statements || stmt_ndx < 1
      || stmt_ndx > session->dimension || !(statement = session->statements[stmt_ndx - 1]))
    {
      return false;
    }

  if (use_plan_cache)
    {
      if (statement->flag.use_plan_cache)
    {
      *use_plan_cache = true;
    }
      else
    {
      *use_plan_cache = false;
    }
    }

  if (use_query_cache)
    {
      if (statement->flag.use_query_cache)
    {
      *use_query_cache = true;
    }
      else
    {
      *use_query_cache = false;
    }
    }

  return true;
}

/*
 * db_get_errors() - This function returns a list of errors that occurred during
 *    compilation. NULL is returned if no errors occurred.
 * returns : compilation error list
 * session(in): session handle
 *
 * note : A call to the db_get_next_error() function can be used to examine
 *    each error. You do not free this list of errors.
 */
DB_SESSION_ERROR *
db_get_errors (DB_SESSION * session)
{
  DB_SESSION_ERROR *result;

  if (!session || !session->parser)
    {
      result = NULL;
    }
  else
    {
      result = pt_get_errors (session->parser);
    }

  return result;
}

/*
 * db_get_next_error() - This function returns the line and column number of
 *    the next error that was passed in the compilation error list.
 * return : next error in compilation error list
 * errors (in) : DB_SESSION_ERROR iterator
 * line(out): source line number of error
 * col(out): source column number of error
 *
 * note : Do not free this list of errors.
 */
DB_SESSION_ERROR *
db_get_next_error (DB_SESSION_ERROR * errors, int *line, int *col)
{
  DB_SESSION_ERROR *result;
  int stmt_no;
  const char *e_msg = NULL;

  if (!errors)
    {
      return NULL;
    }

  result = pt_get_next_error (errors, &stmt_no, line, col, &e_msg);
  if (e_msg)
    {
      er_set (ER_SYNTAX_ERROR_SEVERITY, ARG_FILE_LINE, ER_PT_ERROR, 1, e_msg);
    }

  return result;
}

/*
 * db_get_warnings: This function returns a list of warnings that occurred
 *    during the compilation. NULL is returned if no warnings are found.
 *    A non-NULL return value indicates that one or more warnings occurred
 *    during compilation.
 * returns: DB_SESSION_WARNING iterator if there were any compilation warnings,
 *          NULL, otherwise.
 * session(in): session handle
 *
 * note : Do not free this list of warnings.
 */
DB_SESSION_WARNING *
db_get_warnings (DB_SESSION * session)
{
  DB_SESSION_WARNING *result;

  if (!session || !session->parser)
    {
      result = NULL;
    }
  else
    {
      result = pt_get_warnings (session->parser);
    }

  return result;
}

/*
 * db_get_next_warning: This function returns the line and column number of the
 *    next warning that was passed in the compilation warning list.
 * returns: DB_SESSION_WARNING iterator if there are more compilation warnings
 *          NULL, otherwise.
 * warnings(in) : DB_SESSION_WARNING iterator
 * line(out): source line number of warning
 * col(out): source column number of warning
 *
 * note : Do not free this list of warnings.
 */
DB_SESSION_WARNING *
db_get_next_warning (DB_SESSION_WARNING * warnings, int *line, int *col)
{
  DB_SESSION_WARNING *result;
  int stmt_no;
  const char *e_msg = NULL;

  if (!warnings)
    {
      return NULL;
    }

  result = pt_get_next_error (warnings, &stmt_no, line, col, &e_msg);
  if (e_msg)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_PT_ERROR, 1, e_msg);
    }

  return result;
}

/*
 * db_session_set_holdable () - mark session as holdable
 * return : void
 * session (in) :
 * holdable (in) :
 */
void
db_session_set_holdable (DB_SESSION * session, bool holdable)
{
  if (session == NULL || session->parser == NULL)
    {
      return;
    }
  session->parser->flag.is_holdable = holdable ? 1 : 0;
}

/*
 * db_session_set_xasl_cache_pinned () - mark query as xasl cache pinned
 * return : void
 * session (in) :
 * is_pinned (in) :
 * recompile (in) :
 */
void
db_session_set_xasl_cache_pinned (DB_SESSION * session, bool is_pinned, bool recompile)
{
  if (session == NULL || session->parser == NULL)
    {
      return;
    }
  session->parser->flag.is_xasl_pinned_reference = is_pinned ? 1 : 0;
  session->parser->flag.recompile_xasl_pinned = recompile ? 1 : 0;
}

/*
 * db_session_set_return_generated_keys () - return generated keys for insert
 *                    statements
 * return : void
 * session (in) :
 * return_generated_keys (in) :
 */
void
db_session_set_return_generated_keys (DB_SESSION * session, bool return_generated_keys)
{
  if (session == NULL || session->parser == NULL)
    {
      return;
    }
  session->parser->flag.return_generated_keys = return_generated_keys ? 1 : 0;
}

/*
 * db_get_line_col_of_1st_error() - get the source line & column of first error
 * returns: 1 if there were any query compilation errors, 0, otherwise.
 * session(in) : contains the SQL query that has just been compiled
 * linecol(out): the source line & column of first error if any
 *
 * note : DO NOT USE THIS FUNCTION.  USE db_get_errors & db_get_next_error
 *    instead.  This function is provided for the sole purpose of
 *    facilitating conversion of old code.
 */
int
db_get_line_col_of_1st_error (DB_SESSION * session, DB_QUERY_ERROR * linecol)
{
  if (!session || !session->parser || !pt_has_error (session->parser))
    {
      if (linecol)
    {
      linecol->err_lineno = linecol->err_posno = 0;
    }
      return 0;
    }
  else
    {
      PT_NODE *errors;
      int stmt_no;
      const char *msg;

      errors = pt_get_errors (session->parser);
      if (linecol)
    pt_get_next_error (errors, &stmt_no, &linecol->err_lineno, &linecol->err_posno, &msg);
      return 1;
    }
}

/*
 * db_number_of_input_markers() -
 * return : number of host variable input markers in statement
 * session(in): compilation session
 * stmt(in): statement number of compiled statement
 */
int
db_number_of_input_markers (DB_SESSION * session, int stmt)
{
  PARSER_CONTEXT *parser;
  PT_NODE *statement;
  int result = 0;

  if (!session || !(parser = session->parser) || !session->statements || stmt < 1 || stmt > session->dimension
      || !(statement = session->statements[stmt - 1]))
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      result = er_errid ();
    }
  else
    {
      result = parser->host_var_count;
    }

  return result;
}

/*
 * db_number_of_output_markers() -
 * return : number of host variable output markers in statement
 * session(in): compilation session
 * stmt(in): statement number of compiled statement
 */
int
db_number_of_output_markers (DB_SESSION * session, int stmt)
{
  PARSER_CONTEXT *parser;
  PT_NODE *statement;
  int result = 0;

  if (!session || !(parser = session->parser) || !session->statements || stmt < 1 || stmt > session->dimension
      || !(statement = session->statements[stmt - 1]))
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      result = er_errid ();
    }
  else
    {
      (void) parser_walk_tree (parser, statement, pt_count_output_markers, &result, NULL, NULL);
    }

  return result;
}

/*
 * db_get_input_markers() -
 * return : host variable input markers list in statement
 * session(in): compilation session
 * stmt(in): statement number of compiled statement
 */
DB_MARKER *
db_get_input_markers (DB_SESSION * session, int stmt)
{
  PARSER_CONTEXT *parser;
  PT_NODE *statement;
  DB_MARKER *result = NULL;
  PT_HOST_VARS *hv;

  if (!session || !(parser = session->parser) || !session->statements || stmt < 1 || stmt > session->dimension
      || !(statement = session->statements[stmt - 1]) || pt_has_error (parser))
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      result = NULL;
    }
  else
    {
      hv = pt_host_info (parser, statement);
      result = pt_get_input_host_vars (hv);
      pt_free_host_info (hv);
    }

  return result;
}

/*
 * db_get_output_markers() -
 * return : host variable output markers list in statement
 * session(in): compilation session
 * stmt(in): statement number of compiled statement
 */
DB_MARKER *
db_get_output_markers (DB_SESSION * session, int stmt)
{
  PARSER_CONTEXT *parser;
  PT_NODE *statement;
  DB_MARKER *result = NULL;
  PT_HOST_VARS *hv;

  if (!session || !(parser = session->parser) || !session->statements || stmt < 1 || stmt > session->dimension
      || !(statement = session->statements[stmt - 1]) || pt_has_error (parser))
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      result = NULL;
    }
  else
    {
      hv = pt_host_info (parser, statement);
      result = pt_get_output_host_vars (hv);
      pt_free_host_info (hv);
    }

  return result;
}

/*
 * db_marker_next: This function returns the next marker in the list
 * return : the next host variable (input/output) marker in the list or NULL
 * marker(in): DB_MARKER
 */
DB_MARKER *
db_marker_next (DB_MARKER * marker)
{
  DB_MARKER *result = NULL;

  if (marker)
    {
      result = pt_node_next (marker);
    }

  return result;
}

/*
 * db_marker_index() - This function return the index of an host variable
 *    (input/output) marker
 * return : index of an marker
 * marker(in): DB_MARKER
 */
int
db_marker_index (DB_MARKER * marker)
{
  int result = -1;

  if (marker)
    {
      result = pt_host_var_index (marker);
    }

  return result;
}

/*
 * db_marker_domain() - This function returns the domain of an host variable
 *    (input/output) marker
 * return : domain of marker
 * marker(in): DB_MARKER
 */
DB_DOMAIN *
db_marker_domain (DB_MARKER * marker)
{
  DB_DOMAIN *result = NULL;

  if (marker)
    {
      result = marker->expected_domain;
      if (result == NULL)
    {
      result = pt_node_to_db_domain (NULL, marker, NULL);
    }
    }
  /* it is safet to call pt_node_to_db_domain() without parser */

  return result;
}

/*
 * db_is_input_marker() - Returns true iff it is the host variable input marker
 * return : boolean
 * marker(in): DB_MARKER
 */
bool
db_is_input_marker (DB_MARKER * marker)
{
  bool result = false;

  if (marker)
    {
      result = pt_is_input_hostvar (marker);
    }

  return result;
}

/*
 * db_is_output_marker() - Returns true iff it is the host variable
 *    output marker
 * return : boolean
 * marker(in): DB_MARKER
 */
bool
db_is_output_marker (DB_MARKER * marker)
{
  bool result = false;

  if (marker)
    {
      result = pt_is_output_hostvar (marker);
    }

  return result;
}

/*
 * db_get_query_type_list() - This function returns a type list that describes
 *    the columns of a SELECT statement. This includes the column title, data
 *    type, and size. The statement ID must have been returned by a previously
 *    successful call to the db_compile_statement() function. The query type
 *    list is freed by using the db_query_format_free() function.
 * return : query type.
 * session(in): session handle
 * stmt(in): statement id
 */
DB_QUERY_TYPE *
db_get_query_type_list (DB_SESSION * session, int stmt_ndx)
{
  PT_NODE *statement;
  DB_QUERY_TYPE *qtype;
  CUBRID_STMT_TYPE cmd_type;

  /* obvious error checking - invalid parameter */
  if (!session || !session->parser)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_INVALID_SESSION, 0);
      return NULL;
    }
  /* no statement was given in the session */
  if (session->dimension == 0 || !session->statements)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_EMPTY_STATEMENT, 0);
      return NULL;
    }
  /* invalid parameter */
  statement = session->statements[--stmt_ndx];
  if (stmt_ndx < 0 || stmt_ndx >= session->dimension || !statement)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      return NULL;
    }
  /* check if the statement is compiled and prepared */
  if (session->stage[stmt_ndx] < StatementPreparedStage)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_INVALID_SESSION, 0);
      return NULL;
    }

  /* make DB_QUERY_TYPE structure to return */

  if (statement != NULL && statement->node_type == PT_EXECUTE_PREPARE)
    {
      return db_cp_query_type (session->type_list[stmt_ndx], true);
    }

  cmd_type = pt_node_to_cmd_type (statement);
  if (cmd_type == CUBRID_STMT_SELECT)
    {
      PT_NODE *select_list = pt_get_select_list (session->parser, statement);
      if (pt_length_of_select_list (select_list, EXCLUDE_HIDDEN_COLUMNS) > 0)
    {
      /* duplicate one from stored list */
      qtype = db_cp_query_type (session->type_list[stmt_ndx], true);
    }
      else
    {
      qtype = NULL;
    }
    }
  else
    {
      /* make new one containing single value */
      qtype = db_alloc_query_format (1);
      if (qtype)
    {
      switch (cmd_type)
        {
        case CUBRID_STMT_CALL:
          qtype->db_type = pt_node_to_db_type (statement);
          break;
        case CUBRID_STMT_INSERT:
          /* the type of result of INSERT is object */
          qtype->db_type = DB_TYPE_OBJECT;
          break;
        case CUBRID_STMT_GET_ISO_LVL:
        case CUBRID_STMT_GET_TIMEOUT:
        case CUBRID_STMT_GET_OPT_LVL:
        case CUBRID_STMT_GET_TRIGGER:
          /* the type of result of some command is integer */
          qtype->db_type = DB_TYPE_INTEGER;
          break;
        default:
          break;
        }
    }
    }

  return qtype;
}

/*
 * db_get_query_type_ptr() - This function returns query_type of query result
 * return : result->query_type
 * result(in): query result
 */
DB_QUERY_TYPE *
db_get_query_type_ptr (DB_QUERY_RESULT * result)
{
  return (result->query_type);
}

/*
 * db_get_start_line() - This function returns source line position of
 *    a query statement
 * return : stmt's source line position
 * session(in): contains the SQL query that has been compiled
 * stmt(in): int returned by a successful compilation
 */
int
db_get_start_line (DB_SESSION * session, int stmt)
{
  int retval;
  PARSER_CONTEXT *parser;
  PT_NODE *statement;

  if (!session || !(parser = session->parser) || !session->statements || stmt < 1 || stmt > session->dimension
      || !(statement = session->statements[stmt - 1]))
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      retval = er_errid ();
    }
  else
    {
      retval = pt_statement_line_number (statement);
    }

  return (retval);
}

/*
 * db_get_statement_type() - This function returns query statement node type
 * return : stmt's node type
 * session(in): contains the SQL query that has been compiled
 * stmt(in): statement id returned by a successful compilation
 *
 * todo: is this acceptable? to return both error code and statement type?
 */
int
db_get_statement_type (DB_SESSION * session, int stmt)
{
  int retval;
  PARSER_CONTEXT *parser;
  PT_NODE *statement;

  if (!session || !(parser = session->parser) || !session->statements || stmt < 1 || stmt > session->dimension
      || !(statement = session->statements[stmt - 1]))
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      retval = er_errid ();
    }
  else
    {
      if (statement != NULL && statement->node_type == PT_EXECUTE_PREPARE)
    {
      retval = statement->info.execute.stmt_type;
    }
      else
    {
      retval = pt_node_to_cmd_type (statement);
    }
    }

  return retval;
}

/*
 * db_include_oid() - This function set the session->parser->oid_included flag
 * return : void
 * session(in): the current session context
 * include_oid(in): non-zero means include oid,
 *              zero means don't include it.
 */
void
db_include_oid (DB_SESSION * session, int include_oid)
{
  if (!session)
    {
      return;
    }

  session->include_oid = include_oid;
}

/*
 * db_push_values() - This function set session->parser->host_variables
 *   & host_var_count
 * return : integer, negative implies error.
 * session(in): contains the SQL query that has been compiled
 * count(in): number of elements in in_values table
 * in_values(in): a table of host_variable initialized DB_VALUEs
 */
int
db_push_values (DB_SESSION * session, int count, DB_VALUE * in_values)
{
  PARSER_CONTEXT *parser;

  if (session)
    {
      parser = session->parser;
      if (parser)
    {
      pt_set_host_variables (parser, count, in_values);

      if (parser->host_var_count > 0 && parser->flag.set_host_var == 0)
        {
          if (pt_has_error (session->parser))
        {
          /* This error can occur when using the statement pooling */
          pt_report_to_ersys (session->parser, PT_SEMANTIC);
          /* forget about any previous compilation errors, if any */
          pt_reset_error (session->parser);

          return ER_PT_SEMANTIC;
        }
        }
    }
    }

  return NO_ERROR;
}

/*
 * db_get_hostvars() -
 * return:
 * session(in) :
 */
DB_VALUE *
db_get_hostvars (DB_SESSION * session)
{
  return session->parser->host_variables;
}

/*
 * db_get_lock_classes() -
 * return:
 * session(in) :
 */
char **
db_get_lock_classes (DB_SESSION * session)
{
  if (session == NULL || session->parser == NULL)
    {
      return NULL;
    }

  return (char **) (session->parser->lcks_classes);
}

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

  *continue_walk = PT_CONTINUE_WALK;

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

  if (stmt->info.query.flag.subquery_cached && stmt->xasl_id)
    {
      *err = do_execute_subquery (parser, stmt);
      if (*err != NO_ERROR)
    {
      *continue_walk = PT_STOP_WALK;
    }
    }

  return stmt;
}

/*
 * db_execute_and_keep_statement_local() - This function executes the SQL
 *    statement identified by the stmt argument and returns the result.
 *    The statement ID must have already been returned by a successful call
 *    to the db_open_file() function or the db_open_buffer() function that
 *    came from a call to the db_compile_statement()function. The compiled
 *    statement is preserved, and may be executed again within the same
 *    transaction.
 * return : error status, if execution failed
 *          number of affected objects, if a success & stmt is a SELECT,
 *          UPDATE, DELETE, or INSERT
 * session(in) : contains the SQL query that has been compiled
 * stmt(in) : int returned by a successful compilation
 * result(out): query results descriptor
 */
static int
db_execute_and_keep_statement_local (DB_SESSION * session, int stmt_ndx, DB_QUERY_RESULT ** result)
{
  PARSER_CONTEXT *parser;
  PT_NODE *statement;
  DB_QUERY_RESULT *qres;
  DB_VALUE *val;
  int err = NO_ERROR;
  int server_info_bits;

  SEMANTIC_CHK_INFO sc_info = { NULL, NULL, 0, 0, 0, false, false };
  DB_CLASS_MODIFICATION_STATUS cls_status = DB_CLASS_NOT_MODIFIED;

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

  /* obvious error checking - invalid parameter */
  if (!session || !session->parser)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_INVALID_SESSION, 0);
      return er_errid ();
    }
  /* no statement was given in the session */
  if (session->dimension == 0 || !session->statements)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_EMPTY_STATEMENT, 0);
      return er_errid ();
    }
  /* invalid parameter */
  stmt_ndx--;
  if (stmt_ndx < 0 || stmt_ndx >= session->dimension || !session->statements[stmt_ndx])
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      return er_errid ();
    }

  /* valid host variable was not set before */
  if (session->parser->host_var_count > 0 && session->parser->flag.set_host_var == 0)
    {
      if (pt_has_error (session->parser))
    {
      pt_report_to_ersys (session->parser, PT_SEMANTIC);
      /* forget about any previous compilation errors, if any */
      pt_reset_error (session->parser);
    }
      else
    {
      /* parsed statement has some host variable parameters (input marker '?'), but no host variable (DB_VALUE
       * array) was set by db_push_values() API */
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_UCI_TOO_FEW_HOST_VARS, 0);
    }
      return er_errid ();
    }

  /* if the parser already has something wrong - semantic error */
  if (session->stage[stmt_ndx] < StatementExecutedStage && pt_has_error (session->parser))
    {
      pt_report_to_ersys (session->parser, PT_SEMANTIC);
      return er_errid ();
    }

  /*
   * Execution Stage
   */
  er_clear ();

  parser = session->parser;

  /* initialization */
  assert (parser != NULL);
  parser->query_id = NULL_QUERY_ID;
  parser->flag.is_in_and_list = false;

  /* now, we have a statement to execute */
  statement = session->statements[stmt_ndx];

  /* if the statement was not compiled and prepared, do it */
  if (session->stage[stmt_ndx] < StatementPreparedStage)
    {
      session->stmt_ndx = stmt_ndx;
      if (db_compile_statement_local (session) < 0)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }
    }

  /* forget about any previous compilation errors, if any */
  pt_reset_error (parser);

  /* get sys_date, sys_time, sys_timestamp, sys_datetime values from the server */
  server_info_bits = 0;     /* init */
  if (statement->flag.si_datetime || (statement->node_type == PT_CREATE_ENTITY || statement->node_type == PT_ALTER))
    {
      /* Some create and alter statement require the server timestamp even though it does not explicitly refer
       * timestamp-related pseudocolumns. For instance, create table foo (a timestamp default systimestamp); create
       * view v_foo as select * from foo; */
      db_calculate_current_server_time (parser);

      if (base_server_timeb.time == 0)
    {
      server_info_bits |= SI_SYS_DATETIME;
    }
    }

  if (statement->flag.si_tran_id && DB_IS_NULL (&parser->local_transaction_id))
    {
      /* if it was reset in the previous execution step, fills it now */
      server_info_bits |= SI_LOCAL_TRANSACTION_ID;
    }

  /* request to the server */
  if (server_info_bits)
    {
      err = qp_get_server_info (parser, server_info_bits);
      if (err != NO_ERROR)
    {
      return err;
    }
    }

  if (server_info_bits & SI_SYS_DATETIME)
    {
      db_set_base_server_time (&parser->sys_datetime);
    }

  /* All CTE sub-queries included in the query must be executed first. */
  if (pt_is_allowed_result_cache () && !statement->flag.do_not_use_subquery_cache)
    {
      err = NO_ERROR;
      parser_walk_tree (parser, statement, do_execute_subquery_pre, (void *) &err, NULL, NULL);
      if (err != NO_ERROR)
    {
      return err;
    }
    }

  if (statement->node_type == PT_PREPARE_STATEMENT)
    {
      err = do_process_prepare_statement (session, statement);
      update_execution_values (parser, -1, CUBRID_MAX_STMT_TYPE);
      assert (result == NULL || *result == NULL);
      return err;
    }
  else if (statement->node_type == PT_EXECUTE_PREPARE)
    {
      bool do_recompile = false;
      if (statement->info.execute.stmt_type == CUBRID_STMT_SELECT)
    {
      if (!statement->xasl_id || XASL_ID_IS_NULL (statement->xasl_id) || statement->info.execute.recompile)
        {
          do_recompile = true;
        }
    }
      else
    {
      do_recompile = true;
    }

      if (do_recompile)
    {
      return do_recompile_and_execute_prepared_statement (session, statement, result);
    }
    }
  else if (statement->node_type == PT_DEALLOCATE_PREPARE)
    {
      err = do_process_deallocate_prepare (session, statement);
      update_execution_values (parser, -1, CUBRID_MAX_STMT_TYPE);
      assert (result == NULL || *result == NULL);
      return err;
    }

  /* New interface of do_prepare_statement()/do_execute_statment() is used only when the XASL cache is enabled. If it
   * is disabled, old interface of do_statement() will be used instead. do_statement() makes a XASL everytime rather
   * than using XASL cache. Also, it can be executed in the server without touching the XASL cache by calling
   * prepare_and_execute_query(). */
  do_Trigger_involved = false;

  pt_null_etc (statement);
  if (statement->xasl_id == NULL && ((cls_status = pt_has_modified_class (parser, statement)) != DB_CLASS_NOT_MODIFIED))
    {
      if (cls_status == DB_CLASS_MODIFIED)
    {
      err = ER_QPROC_INVALID_XASLNODE;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err, 0);
    }
      else
    {
      assert (cls_status == DB_CLASS_ERROR);
      assert (er_errid () != NO_ERROR);
      err = er_errid ();
    }
    }
  else if (prm_get_integer_value (PRM_ID_XASL_CACHE_MAX_ENTRIES) > 0 && statement->flag.cannot_prepare == 0)
    {
      /* now, execute the statement by calling do_execute_statement() */
      err = do_execute_statement (parser, statement);
      if (((err == ER_QPROC_XASLNODE_RECOMPILE_REQUESTED || err == ER_QPROC_INVALID_XASLNODE
        || err == ER_QPROC_RESULT_CACHE_INVALID) && session->stage[stmt_ndx] == StatementPreparedStage)
      || (err == ER_QPROC_XASLNODE_RECOMPILE_REQUESTED && session->stage[stmt_ndx] == StatementExecutedStage))
    {
      /* The cache entry was deleted before 'execute' */
      if (statement->xasl_id)
        {
          pt_free_statement_xasl_id (statement);
        }

      if (err == ER_QPROC_RESULT_CACHE_INVALID)
        {
          statement->flag.do_not_use_subquery_cache = 1;
          statement->flag.recompile = 1;
          if (statement->node_type == PT_EXECUTE_PREPARE)
        {
          statement->info.execute.recompile = 1;
          return do_recompile_and_execute_prepared_statement (session, statement, result);
        }
        }

      cls_status = pt_has_modified_class (parser, statement);
      if (err == ER_QPROC_RESULT_CACHE_INVALID || cls_status == DB_CLASS_NOT_MODIFIED)
        {
          /* forget all errors */
          er_clear ();
          pt_reset_error (parser);
          parser->query_id = NULL_QUERY_ID; /* reset to re-try */

          /* retry the statement by calling do_prepare/execute_statement() */
          if (do_prepare_statement (parser, statement) == NO_ERROR)
        {
          err = do_execute_statement (parser, statement);
        }
        }
      else if (cls_status == DB_CLASS_MODIFIED)
        {
          err = ER_QPROC_INVALID_XASLNODE;
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err, 0);
        }
      else
        {
          assert (cls_status == DB_CLASS_ERROR);
          assert (er_errid () != NO_ERROR);
          err = er_errid ();
        }
    }
    }
  else
    {
      /* bind and resolve host variables */
      assert (parser->host_var_count >= 0 && parser->auto_param_count >= 0);
      if (parser->host_var_count > 0)
    {
      assert (parser->flag.set_host_var == 1);
    }
      if (parser->host_var_count > 0 || parser->auto_param_count > 0)
    {
      /* In this case, pt_bind_values_to_hostvars() will change PT_HOST_VAR node. Must duplicate the statement and
       * execute with the new one and free the copied one before returning */
      statement = parser_copy_tree_list (parser, statement);
      statement = mq_reset_ids_in_statement (parser, statement);

      sc_info.top_node = statement;
      sc_info.donot_fold = false;

      if (!(statement = pt_bind_values_to_hostvars (parser, statement))
          || !(statement = pt_resolve_names (parser, statement, &sc_info))
          || !(statement = pt_semantic_type (parser, statement, &sc_info)))
        {
          /* something wrong */
          if (er_errid () == NO_ERROR)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_DO_UNKNOWN_HOSTVAR_TYPE, 0);
        }
          if (pt_has_error (parser))
        {
          pt_report_to_ersys_with_statement (parser, PT_SYNTAX, statement);
          pt_reset_error (parser);
        }
          if (statement != session->statements[stmt_ndx])
        {
          parser_free_tree (parser, statement);
        }
          assert (er_errid () != NO_ERROR);
          return er_errid ();
        }
    }

      err = do_statement (parser, statement);
    }

  do_Trigger_involved = false;
  if (err < 0)
    {
      /* Do not override original error id with */
      if (er_errid () == NO_ERROR && pt_has_error (parser) && err != ER_QPROC_INVALID_XASLNODE)
    {
      pt_report_to_ersys_with_statement (parser, PT_EXECUTION, statement);
      err = er_errid ();
    }
      /* free the allocated list_id area before leaving */
      pt_free_query_etc_area (parser, statement);
    }

  /* so now, the statement is executed */
  session->stage[stmt_ndx] = StatementExecutedStage;

  /* execution succeeded, maybe. process result of the query */
  if (result && !(err < 0))
    {
      qres = NULL;

      if (statement->flag.clt_cache_reusable)
    {
      qres = pt_make_cache_hit_result_descriptor ();
      if (qres == NULL)
        {
          assert (er_errid () != NO_ERROR);
          err = er_errid ();
        }
    }
      else
    {
      CUBRID_STMT_TYPE stmt_type = pt_node_to_cmd_type (statement);
      switch (stmt_type)
        {
        case CUBRID_STMT_SELECT:
        case CUBRID_STMT_EXECUTE_PREPARE:
          /* Check whether pt_new_query_result_descriptor() fails. Similar tests are required for
           * CUBRID_STMT_INSERT and CUBRID_STMT_CALL cases. */
          qres = pt_new_query_result_descriptor (parser, statement);
          if (qres)
        {
          /* get number of rows as result */
          err = db_query_tuple_count (qres);
          qres->query_type = db_cp_query_type (session->type_list[stmt_ndx], false);
          qres->res.s.stmt_id = stmt_ndx;
        }
          else
        {
          assert (er_errid () != NO_ERROR);
          err = er_errid ();
        }
          break;

        case CUBRID_STMT_DO:
          pt_free_query_etc_area (parser, statement);
          break;

        case CUBRID_STMT_GET_ISO_LVL:
        case CUBRID_STMT_GET_TIMEOUT:
        case CUBRID_STMT_GET_OPT_LVL:
        case CUBRID_STMT_GET_TRIGGER:
        case CUBRID_STMT_EVALUATE:
        case CUBRID_STMT_CALL:
        case CUBRID_STMT_INSERT:
        case CUBRID_STMT_GET_STATS:
          /* csql (in csql.c) may throw away any non-null *result, but we create a DB_QUERY_RESULT structure anyway
           * for other callers of db_execute that use the *result like esql_cli.c */
          if (pt_is_server_insert_with_generated_keys (parser, statement))
        {
          qres = pt_new_query_result_descriptor (parser, statement);
          if (qres)
            {
              /* get number of rows as result */
              qres->query_type = db_cp_query_type (session->type_list[stmt_ndx], false);
              qres->res.s.stmt_id = stmt_ndx;
            }
          else
            {
              assert (er_errid () != NO_ERROR);
              err = er_errid ();
            }
          break;
        }

          if (stmt_type == CUBRID_STMT_INSERT
          && (statement->info.insert.server_allowed == SERVER_INSERT_IS_ALLOWED))
        {
          val = db_value_create ();
          if (val == NULL)
            {
              assert (er_errid () != NO_ERROR);
              err = er_errid ();
              break;
            }
          db_make_object (val, NULL);
        }
          else
        {
          val = (DB_VALUE *) pt_node_etc (statement);
        }

          if (val)
        {
          /* got a result, so use it */
          qres = db_get_db_value_query_result (val);
          if (qres)
            {
              /* get number of rows as result */
              int row_count = err;
              err = db_query_tuple_count (qres);
              /* We have a special case for REPLACE INTO: pt_node_etc (statement) holds only the inserted row
               * but we might have done a delete before. For this case, if err>row_count we will not change the
               * row count */
              if (stmt_type == CUBRID_STMT_INSERT)
            {
              if ((DB_VALUE_DOMAIN_TYPE (val) == DB_TYPE_OBJECT && DB_IS_NULL (val))
                  || (statement->info.insert.do_replace && row_count > err))
                {
                  err = row_count;
                }
            }
            }
          else
            {
              assert (er_errid () != NO_ERROR);
              err = er_errid ();
            }

          /* db_get_db_value_query_result copied val, so free val */
          db_value_free (val);
          pt_null_etc (statement);
        }
          else
        {
          /* avoid changing err. it should have been meaningfully set. if err = 0, uci_static will set SQLCA to
           * SQL_NOTFOUND! */
        }
          break;

        default:
          break;
        }           /* switch (pt_node_to_cmd_type()) */

    }           /* else */

      *result = qres;
    }               /* if (result) */

  /* Do not override original error id with */
  /* last error checking */
  if (er_errid () == NO_ERROR && pt_has_error (parser) && err != ER_QPROC_INVALID_XASLNODE)
    {
      pt_report_to_ersys_with_statement (parser, PT_EXECUTION, statement);
      err = er_errid ();
    }

  /* reset the parser values */
  if (statement->flag.si_datetime)
    {
      db_make_null (&parser->sys_datetime);
      db_make_null (&parser->sys_epochtime);
    }
  if (statement->flag.si_tran_id)
    {
      db_make_null (&parser->local_transaction_id);
    }

  update_execution_values (parser, err, pt_node_to_cmd_type (statement));

  /* free if the statement was duplicated for host variable binding */
  if (statement != session->statements[stmt_ndx])
    {
      parser_free_tree (parser, statement);
    }

  return err;
}

static void
update_execution_values (PARSER_CONTEXT * parser, int result, CUBRID_STMT_TYPE statement_type)
{
  if (result < 0)
    {
      parser->execution_values.row_count = -1;
    }
  else if (statement_type == CUBRID_STMT_UPDATE || statement_type == CUBRID_STMT_INSERT
       || statement_type == CUBRID_STMT_DELETE)
    {
      parser->execution_values.row_count = result;
    }
  else
    {
      parser->execution_values.row_count = -1;
    }
  db_update_row_count_cache (parser->execution_values.row_count);
}

static void
copy_execution_values (EXECUTION_STATE_VALUES * source, EXECUTION_STATE_VALUES * destination)
{
  assert (destination != NULL && source != NULL);
  destination->row_count = source->row_count;
}

static int
values_list_to_values_array (PARSER_CONTEXT * parser, PT_NODE * values_list, DB_VALUE_ARRAY * values_array)
{
  DB_VALUE_ARRAY values;
  PT_NODE *current_value = values_list;
  int i = 0;
  int err = NO_ERROR;

  values.size = 0;
  values.vals = NULL;

  if (parser == NULL || values_array == NULL || values_array->size != 0 || values_array->vals != NULL)
    {
      assert (false);
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      err = er_errid ();
      goto error_exit;
    }

  if (values_list == NULL)
    {
      return NO_ERROR;
    }
  while (current_value != NULL)
    {
      values.size++;
      current_value = current_value->next;
    }

  values.vals = (DB_VALUE *) malloc (values.size * sizeof (DB_VALUE));
  if (values.vals == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, values.size * sizeof (DB_VALUE));
      err = er_errid ();
      goto error_exit;
    }

  for (i = 0; i < values.size; ++i)
    {
      db_make_null (&values.vals[i]);
    }
  for (current_value = values_list, i = 0; current_value != NULL; current_value = current_value->next, ++i)
    {
      if (current_value->node_type == PT_EXPR && current_value->info.expr.op == PT_EVALUATE_VARIABLE)
    {
      /* this is a session variable */
      DB_VALUE val;
      DB_VALUE *name;

      assert (current_value->info.expr.arg1->node_type == PT_VALUE);

      name = pt_value_to_db (parser, current_value->info.expr.arg1);
      db_make_null (&val);
      if (db_get_variable (name, &val) != NO_ERROR)
        {
          assert (er_errid () != NO_ERROR);
          err = er_errid ();
          goto error_exit;
        }
      pr_clone_value (&val, &values.vals[i]);
      pr_clear_value (&val);
    }
      else
    {
      DB_VALUE *db_val = NULL;

      db_val = pt_value_to_db (parser, current_value);
      if (db_val == NULL)
        {
          er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
          err = er_errid ();
          goto error_exit;
        }
      pr_clone_value (db_val, &values.vals[i]);
    }
    }

  values_array->size = values.size;
  values_array->vals = values.vals;
  values.size = 0;
  values.vals = NULL;

  return err;

error_exit:

  if (values.vals != NULL)
    {
      db_value_clear_array (&values);
      free_and_init (values.vals);
      values.size = 0;
    }
  return err;
}

static int
set_prepare_info_into_list (DB_PREPARE_INFO * prepare_info, PT_NODE * statement)
{
  int length = 0;
  PT_NODE *name = NULL;

  assert (prepare_info->into_list == NULL);

  prepare_info->into_count = 0;

  if (pt_node_to_cmd_type (statement) != CUBRID_STMT_SELECT)
    {
      return NO_ERROR;
    }

  if (statement->info.query.into_list == NULL)
    {
      return NO_ERROR;
    }

  length = pt_length_of_list (statement->info.query.into_list);
  if (length == 0)
    {
      return NO_ERROR;
    }

  prepare_info->into_list = (char **) malloc (length * sizeof (char *));
  if (prepare_info->into_list == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, length * sizeof (char *));
      goto error;
    }
  name = statement->info.query.into_list;
  length = 0;
  while (name)
    {
      if (PT_IS_NAME_NODE (name))
    {
      if (name->info.name.original == NULL)
        {
          prepare_info->into_list[length] = NULL;
        }
      else
        {
          char *into_name = (char *) malloc (strlen (name->info.name.original) + 1);
          if (into_name == NULL)
        {
          er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
              (size_t) (strlen (name->info.name.original) + 1));
          goto error;
        }
          memcpy (into_name, name->info.name.original, strlen (name->info.name.original));
          into_name[strlen (name->info.name.original)] = 0;
          prepare_info->into_list[length] = into_name;
        }
    }
      else
    {
      prepare_info->into_list[length] = NULL;
    }
      length++;
      name = name->next;
    }

  prepare_info->into_count = length;
  return NO_ERROR;

error:
  if (prepare_info->into_list != NULL)
    {
      int i = 0;
      for (i = 0; i < length; i++)
    {
      if (prepare_info->into_list[i] != NULL)
        {
          free_and_init (prepare_info->into_list[i]);
        }
    }
      free_and_init (prepare_info->into_list);
    }
  return ER_FAILED;
}

static PT_NODE *
char_array_to_name_list (PARSER_CONTEXT * parser, char **names, int length)
{
  PT_NODE *name = NULL;
  PT_NODE *list = NULL;
  int i;

  for (i = 0; i < length; i++)
    {
      name = pt_name (parser, names[i]);
      list = parser_append_node (name, list);
    }

  return list;
}

static DB_PREPARE_SUBQUERY_INFO *
set_prepare_subquery_info (PT_NODE * query, DB_PREPARE_SUBQUERY_INFO * info, int num_query)
{
  int i, q = num_query;

  if (num_query % 4 == 0)   /* need to realloc subquery info every 4 */
    {
      int alloc_size = sizeof (DB_PREPARE_SUBQUERY_INFO) * (num_query / 4 + 1) * 4;

      DB_PREPARE_SUBQUERY_INFO *alloc = (DB_PREPARE_SUBQUERY_INFO *) realloc (info, alloc_size);

      if (alloc == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, alloc_size);
      goto err_exit;
    }

      info = alloc;
    }

  XASL_ID_COPY (&info[q].xasl_id, query->xasl_id);
  info[q].host_var_count = query->sub_host_var_count;
  info[q].host_var_index = NULL;

  if (query->sub_host_var_count > 0)
    {
      info[q].host_var_index = (int *) malloc (sizeof (int) * query->sub_host_var_count);
      if (info[q].host_var_index == NULL)
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1,
          sizeof (int) * query->sub_host_var_count);
      goto err_exit;
    }

      memcpy (info[q].host_var_index, query->sub_host_var_index, query->sub_host_var_count * sizeof (int));
    }

  return info;

err_exit:
  if (info != NULL)
    {
      for (i = 0; i < num_query; i++)
    {
      if (info[i].host_var_index)
        {
          free (info[i].host_var_index);
        }
    }
      free (info);
    }

  return NULL;
}

static PT_NODE *
do_process_prepare_subquery_pre (PARSER_CONTEXT * parser, PT_NODE * stmt, void *arg, int *continue_walk)
{
  DB_PREPARE_INFO *prepare_info;

  *continue_walk = PT_CONTINUE_WALK;

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

  *continue_walk = PT_CONTINUE_WALK;

  prepare_info = (DB_PREPARE_INFO *) arg;

  if (stmt->node_type == PT_SELECT && stmt->xasl_id != NULL && stmt->info.query.flag.subquery_cached)
    {
      prepare_info->subquery_info =
    set_prepare_subquery_info (stmt, prepare_info->subquery_info, prepare_info->subquery_num);
      if (prepare_info->subquery_info == NULL)
    {
      *continue_walk = PT_STOP_WALK;
      return stmt;
    }

      prepare_info->subquery_num++;
    }

  return stmt;
}

/*
 * do_process_prepare_statement () - execute a 'PREPARE STMT FROM ...'
 *                   statement
 * return:   error code or NO_ERROR
 * session (in)   : client session for this statement
 * statement (in) : the statement
 */
static int
do_process_prepare_statement (DB_SESSION * session, PT_NODE * statement)
{
  DB_PREPARE_INFO prepare_info;
  DB_SESSION *prepared_session = NULL;
  int prepared_statement_ndx = 0;
  PT_NODE *prepared_stmt = NULL;
  const char *const name = statement->info.prepare.name->info.name.original;
  const char *const statement_literal = (char *) statement->info.prepare.statement->info.value.data_value.str->bytes;
  int err = NO_ERROR;
  char *stmt_info = NULL;
  int i, info_len = 0;
  assert (statement->node_type == PT_PREPARE_STATEMENT);
  db_init_prepare_info (&prepare_info);

  prepared_session = db_open_buffer_local (statement_literal);
  if (prepared_session == NULL)
    {
      assert (er_errid () != NO_ERROR);
      err = er_errid ();
      goto cleanup;
    }

  /* we need to copy all the relevant settings */
  prepared_session->include_oid = session->include_oid;

  prepared_statement_ndx = db_compile_statement_local (prepared_session);
  if (prepared_statement_ndx < 0)
    {
      err = prepared_statement_ndx;
      goto cleanup;
    }

  err = db_check_single_query (prepared_session);
  if (err != NO_ERROR)
    {
      err = ER_IT_MULTIPLE_STATEMENT;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IT_MULTIPLE_STATEMENT, 0);
      goto cleanup;
    }

  assert (prepared_statement_ndx == 1);
  assert (prepared_session->dimension == 1);
  assert (prepared_session->statements[0] != NULL);

  prepared_stmt = prepared_session->statements[0];

  if (!is_allowed_as_prepared_statement (prepared_stmt))
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_IT_IS_DISALLOWED_AS_PREPARED, 0);
      err = ER_FAILED;
      goto cleanup;
    }

  if (prepared_session->parser->host_var_count > 0 && !is_allowed_as_prepared_statement_with_hv (prepared_stmt))
    {
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_CANNOT_PREPARE_WITH_HOST_VAR, 1,
          pt_show_node_type (prepared_session->statements[0]));
      err = ER_FAILED;
      goto cleanup;
    }

  /* set statement literal */
  prepare_info.statement = (char *) statement_literal;
  /* set columns */
  prepare_info.columns = prepared_session->type_list[0];
  /* set statement type */
  prepare_info.stmt_type = pt_node_to_cmd_type (prepared_stmt);
  /* set host variables */
  prepare_info.host_variables.size =
    prepared_session->parser->host_var_count + prepared_session->parser->auto_param_count;
  prepare_info.host_variables.vals = prepared_session->parser->host_variables;
  prepare_info.host_var_expected_domains = prepared_session->parser->host_var_expected_domains;
  /* set autoparam count */
  prepare_info.auto_param_count = prepared_session->parser->auto_param_count;
  /* set recompile */
  prepare_info.recompile = prepared_stmt->flag.recompile;

  if (prepare_info.stmt_type == CUBRID_STMT_SELECT)
    {
      /* set OIDs included */
      prepare_info.oids_included = prepared_stmt->info.query.oids_included;

      /* set result cache */
      prepare_info.do_cache = prepared_stmt->info.query.flag.do_cache;
    }

  err = set_prepare_info_into_list (&prepare_info, prepared_stmt);
  if (err != NO_ERROR)
    {
      goto cleanup;
    }

  parser_walk_tree (prepared_session->parser, prepared_stmt, do_process_prepare_subquery_pre, &prepare_info, NULL,
            NULL);
  if ((err = er_errid ()) != NO_ERROR)
    {
      goto cleanup;
    }

  err = db_pack_prepare_info (&prepare_info, &stmt_info);
  if (err < 0)
    {
      goto cleanup;
    }

  info_len = err;

  err = csession_create_prepared_statement (name, prepared_stmt->alias_print, stmt_info, info_len);

cleanup:
  if (err < 0 && name != NULL)
    {
      /* clear the previously cached one with the same name if exists */
      er_stack_push ();
      csession_delete_prepared_statement (name);
      er_stack_pop ();
    }

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

  if (prepared_session)
    {
      db_close_session_local (prepared_session);
    }

  if (prepare_info.into_list != NULL)
    {
      for (i = 0; i < prepare_info.into_count; i++)
    {
      free_and_init (prepare_info.into_list[i]);
    }
      free_and_init (prepare_info.into_list);
    }

  if (prepare_info.subquery_info != NULL)
    {
      for (i = 0; i < prepare_info.subquery_num; i++)
    {
      free_and_init (prepare_info.subquery_info[i].host_var_index);
    }
      free_and_init (prepare_info.subquery_info);
    }

  return err;
}

/*
 * do_get_prepared_statement_info () - get prepared statement information
 * return : error code or NO_ERROR
 * session (in) : client session context
 * stmt_idx (in) : statement index
 */
static int
do_get_prepared_statement_info (DB_SESSION * session, int stmt_idx, int *subquery_num,
                DB_PREPARE_SUBQUERY_INFO ** subquery_info)
{
  const char *name = NULL;
  char *stmt_info = NULL;
  XASL_ID xasl_id;
  int err = NO_ERROR, i = 0;
  DB_VALUE *hv = NULL;
  PT_NODE *statement = session->statements[stmt_idx];
  PARSER_CONTEXT *parser = session->parser;
  DB_PREPARE_INFO prepare_info;
  DB_QUERY_TYPE *col = NULL;
  XASL_NODE_HEADER xasl_header;

  assert (pt_node_to_cmd_type (statement) == CUBRID_STMT_EXECUTE_PREPARE);
  db_init_prepare_info (&prepare_info);

  name = statement->info.execute.name->info.name.original;
  err = csession_get_prepared_statement (name, &xasl_id, &stmt_info, &xasl_header);
  if (err != NO_ERROR)
    {
      return err;
    }

  db_unpack_prepare_info (&prepare_info, stmt_info);
  *subquery_num = prepare_info.subquery_num;
  *subquery_info = prepare_info.subquery_info;

  statement->info.execute.column_count = 0;
  col = prepare_info.columns;
  while (col)
    {
      statement->info.execute.column_count++;
      col = col->next;
    }

  /* set session type list */
  session->type_list[stmt_idx] = prepare_info.columns;

  statement->info.execute.into_list =
    char_array_to_name_list (session->parser, prepare_info.into_list, prepare_info.into_count);

  statement->info.execute.stmt_type = prepare_info.stmt_type;

  /* set query */
  statement->info.execute.query = pt_make_string_value (parser, prepare_info.statement);
  if (statement->info.execute.query == NULL)
    {
      PT_INTERNAL_ERROR (parser, "allocate new node");
    }
  statement->info.execute.recompile = prepare_info.recompile;
  statement->info.execute.do_cache = prepare_info.do_cache;
  statement->info.execute.oids_included = prepare_info.oids_included;

  XASL_ID_COPY (&statement->info.execute.xasl_id, &xasl_id);

  /* restore host variables used by this statement */
  for (i = 0, hv = parser->host_variables; i < parser->host_var_count + parser->auto_param_count; i++, hv++)
    {
      pr_clear_value (hv);
    }

  if (parser->host_variables)
    {
      free_and_init (parser->host_variables);
    }

  if (parser->host_var_expected_domains)
    {
      free_and_init (parser->host_var_expected_domains);
    }

  parser->auto_param_count = 0;
  parser->host_var_count = 0;

  parser->auto_param_count = prepare_info.auto_param_count;
  parser->host_variables = prepare_info.host_variables.vals;
  parser->host_var_expected_domains = prepare_info.host_var_expected_domains;
  parser->host_var_count = prepare_info.host_variables.size - prepare_info.auto_param_count;

  err = do_set_user_host_variables (session, statement->info.execute.using_list);
  if (err != NO_ERROR)
    {
      goto cleanup;
    }

  parser->host_var_count += prepare_info.auto_param_count;
  parser->auto_param_count = 0;
  parser->flag.set_host_var = 1;

  /* Like optimization check: if host-variables were used in LIKE conditions, check if query needs to be recompiled
   * to remove LIKE conditions.
   * Multi range optimization check: if host-variables were used (not auto-parameterized), the orderby_num () limit may
   * change and invalidate or validate multi range optimization. Check if query needs to be recompiled. */
  if (!XASL_ID_IS_NULL (&xasl_id)   /* xasl_id should not be null */
      && !statement->info.execute.recompile /* recompile is already planned */
      && (prepare_info.host_variables.size > prepare_info.auto_param_count))
    {
      if (xasl_header.xasl_flag & LIKE_RECOMPILE_CANDIDATE && prm_get_bool_value (PRM_ID_HOSTVAR_PEEKING))
    {
      if (db_check_where_need_recompile (parser, statement, xasl_header.xasl_flag))
        {
          XASL_ID_SET_NULL (&statement->info.execute.xasl_id);
        }
    }

      /* query has to be multi range opt candidate */
      if (xasl_header.xasl_flag & (MRO_CANDIDATE | MRO_IS_USED | SORT_LIMIT_CANDIDATE | SORT_LIMIT_USED))
    {
      if (db_check_limit_need_recompile (parser, statement, xasl_header.xasl_flag))
        {
          /* need recompile, set XASL_ID to NULL */
          XASL_ID_SET_NULL (&statement->info.execute.xasl_id);
        }
    }
    }

cleanup:
  if (stmt_info != NULL)
    {
      free_and_init (stmt_info);
    }
  if (prepare_info.statement != NULL)
    {
      free_and_init (prepare_info.statement);
    }
  if (prepare_info.into_list != NULL)
    {
      for (i = 0; i < prepare_info.into_count; i++)
    {
      if (prepare_info.into_list[i] != NULL)
        {
          free_and_init (prepare_info.into_list[i]);
        }
    }
      free_and_init (prepare_info.into_list);
    }
  return err;
}

/*
 * do_cast_host_variables_to_expected_domain () - After compilation phase,
 *                        cast all host variables to
 *                        their expected domains
 *
 * return   : error code
 * session (in) : db_session
 */
static int
do_cast_host_variables_to_expected_domain (DB_SESSION * session)
{
  int hv_count = session->parser->host_var_count;
  DB_VALUE *host_vars = session->parser->host_variables;
  TP_DOMAIN **expected_domains = session->parser->host_var_expected_domains;
  DB_VALUE *hv = NULL;
  TP_DOMAIN *hv_dom = NULL, *d = NULL;
  int i = 0;

  for (i = 0; i < hv_count; i++)
    {
      int prec;
      DB_TYPE typ;

      hv = &host_vars[i];
      typ = db_value_type (hv);
      prec = db_value_precision (hv);
      hv_dom = expected_domains[i];
      if (TP_DOMAIN_TYPE (hv_dom) == DB_TYPE_UNKNOWN || hv_dom->type->id == DB_TYPE_ENUMERATION)
    {
      /* skip casting enum and unknown type values */
      continue;
    }
      if (tp_value_cast_preserve_domain (hv, hv, hv_dom, false, true) != DOMAIN_COMPATIBLE)
    {
      d = pt_type_enum_to_db_domain (pt_db_to_type_enum (TP_DOMAIN_TYPE (hv_dom)));
      PT_ERRORmf2 (session->parser, NULL, MSGCAT_SET_PARSER_SEMANTIC, MSGCAT_SEMANTIC_CANT_COERCE_TO, "host var",
               d);
      tp_domain_free (d);
      pt_report_to_ersys (session->parser, PT_EXECUTION);
      pt_reset_error (session->parser);
      return ER_PT_EXECUTE;
    }

      if (TP_IS_CHAR_TYPE (hv_dom->type->id))
    {
      if (hv_dom->type->id != typ && (typ == DB_TYPE_VARCHAR))
        {
          db_value_domain_init (hv, typ, prec, 0);
        }
    }
    }

  session->parser->flag.set_host_var = 1;

  return NO_ERROR;
}

/*
 * do_set_user_host_variables () - Set host variables values in parser from
 *                 using_list
 *
 * return      : error code
 * session (in)    : db_session
 * using_list (in) : list of db_values
 */
static int
do_set_user_host_variables (DB_SESSION * session, PT_NODE * using_list)
{
  DB_VALUE_ARRAY values_array;
  int err = NO_ERROR;

  values_array.size = 0;
  values_array.vals = NULL;

  if (values_list_to_values_array (session->parser, using_list, &values_array) != NO_ERROR)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  if (session->parser->host_var_count != values_array.size)
    {
      err = ER_IT_INCORRECT_HOSTVAR_COUNT;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, err, 2, values_array.size, session->parser->host_var_count);
    }
  else
    {
      err = db_push_values (session, values_array.size, values_array.vals);
    }

  db_value_clear_array (&values_array);
  free_and_init (values_array.vals);
  values_array.size = 0;

  return err;
}


/*
 * db_check_where_need_recompile () - Check if statement has to be recompiled
 *                    for where optimizations with supplied
 *                    where value
 *
 * return     : true if recompile is needed, false otherwise
 * parser (in)    : parser context for statement
 * statement (in) : execute prepare statement
 *
 */
static bool
db_check_where_need_recompile (PARSER_CONTEXT * parent_parser, PT_NODE * statement, int xasl_flag)
{
  DB_SESSION *session = NULL;
  PT_NODE *query = NULL;
  bool do_recompile = false;
  DB_VALUE *save_host_variables = NULL;
  TP_DOMAIN **save_host_var_expected_domains = NULL;
  int save_host_var_count, save_auto_param_count;

  if (statement->node_type != PT_EXECUTE_PREPARE)
    {
      /* statement must be execute prepare */
      return false;
    }
  if (statement->info.execute.stmt_type != CUBRID_STMT_SELECT)
    {
      return false;
    }

  assert (statement->info.execute.query->node_type == PT_VALUE);
  assert (statement->info.execute.query->type_enum == PT_TYPE_CHAR);

  session = db_open_buffer_local ((char *) statement->info.execute.query->info.value.data_value.str->bytes);
  if (session == NULL)
    {
      /* error opening session */
      return false;
    }

  if (session->dimension != 1)
    {
      /* need full recompile */
      do_recompile = true;
      goto exit;
    }
  query = session->statements[0];
  assert (PT_IS_QUERY (query));

  /* set host variable info */
  save_auto_param_count = session->parser->auto_param_count;
  save_host_var_count = session->parser->host_var_count;
  save_host_variables = session->parser->host_variables;
  save_host_var_expected_domains = session->parser->host_var_expected_domains;

  session->parser->host_variables = parent_parser->host_variables;
  session->parser->host_var_expected_domains = parent_parser->host_var_expected_domains;
  session->parser->host_var_count = parent_parser->host_var_count;
  session->parser->auto_param_count = parent_parser->auto_param_count;
  session->parser->flag.set_host_var = 1;

  if (pt_recompile_for_like_optimizations (session->parser, query, xasl_flag))
    {
      do_recompile = true;
    }

  /* restore host variable info */
  session->parser->host_variables = save_host_variables;
  session->parser->host_var_expected_domains = save_host_var_expected_domains;
  session->parser->auto_param_count = save_auto_param_count;
  session->parser->host_var_count = save_host_var_count;
  session->parser->flag.set_host_var = 0;

exit:
  /* clean up */
  if (session != NULL)
    {
      db_close_session (session);
    }
  return do_recompile;
}


/*
 * db_check_limit_need_recompile () - Check if statement has to be recompiled
 *                    for limit optimizations with supplied
 *                    limit value
 *
 * return     : true if recompile is needed, false otherwise
 * parser (in)    : parser context for statement
 * statement (in) : execute prepare statement
 * xasl_flag (in) : flag specifying limit optimizations used in XASL
 *
 * NOTE: This function attempts to evaluate superior limit for orderby_num ()
 *   without doing a full statement recompile.
 */
static bool
db_check_limit_need_recompile (PARSER_CONTEXT * parent_parser, PT_NODE * statement, int xasl_flag)
{
  DB_SESSION *session = NULL;
  PT_NODE *query = NULL;
  bool do_recompile = false;
  DB_VALUE *save_host_variables = NULL;
  TP_DOMAIN **save_host_var_expected_domains = NULL;
  int save_host_var_count, save_auto_param_count;

  if (statement->node_type != PT_EXECUTE_PREPARE)
    {
      /* statement must be execute prepare */
      return false;
    }
  if (statement->info.execute.stmt_type != CUBRID_STMT_SELECT)
    {
      return false;
    }

  assert (statement->info.execute.query->node_type == PT_VALUE);
  assert (statement->info.execute.query->type_enum == PT_TYPE_CHAR);

  session = db_open_buffer_local ((char *) statement->info.execute.query->info.value.data_value.str->bytes);
  if (session == NULL)
    {
      /* error opening session */
      return false;
    }

  if (session->dimension != 1)
    {
      /* need full recompile */
      do_recompile = true;
      goto exit;
    }
  query = session->statements[0];
  assert (PT_IS_QUERY (query));

  /* set host variable info */
  save_auto_param_count = session->parser->auto_param_count;
  save_host_var_count = session->parser->host_var_count;
  save_host_variables = session->parser->host_variables;
  save_host_var_expected_domains = session->parser->host_var_expected_domains;

  session->parser->host_variables = parent_parser->host_variables;
  session->parser->host_var_expected_domains = parent_parser->host_var_expected_domains;
  session->parser->host_var_count = parent_parser->host_var_count;
  session->parser->auto_param_count = parent_parser->auto_param_count;
  session->parser->flag.set_host_var = 1;

  if (pt_recompile_for_limit_optimizations (session->parser, query, xasl_flag))
    {
      /* need recompile */
      do_recompile = true;
    }

  /* restore host variable info */
  session->parser->host_variables = save_host_variables;
  session->parser->host_var_expected_domains = save_host_var_expected_domains;
  session->parser->auto_param_count = save_auto_param_count;
  session->parser->host_var_count = save_host_var_count;
  session->parser->flag.set_host_var = 0;

exit:
  /* clean up */
  if (session != NULL)
    {
      db_close_session (session);
    }
  return do_recompile;
}

/*
 * do_recompile_and_execute_prepared_statement () - compile and execute a
 *                          prepared statement
 * return : error code or NO_ERROR
 * session (in)   : client session context
 * statement (in) : statement to be executed
 * result (out)   : execution result
 */
static int
do_recompile_and_execute_prepared_statement (DB_SESSION * session, PT_NODE * statement, DB_QUERY_RESULT ** result)
{
  int err = NO_ERROR;
  int idx = 0;
  DB_SESSION *new_session = NULL;
  assert (statement->info.execute.query->node_type == PT_VALUE);
  assert (statement->info.execute.query->type_enum == PT_TYPE_CHAR);

  new_session = db_open_buffer_local ((char *) statement->info.execute.query->info.value.data_value.str->bytes);
  if (new_session == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  new_session->is_subsession_for_prepared = true;

  /* add the new session to the subsessions list */
  if (session->next == NULL)
    {
      session->next = new_session;
    }
  else
    {
      new_session->next = session->next;
      session->next = new_session;
    }

  if (statement->info.execute.recompile)
    {
      new_session->statements[0]->flag.do_not_use_subquery_cache = 1;
      new_session->statements[0]->flag.recompile = statement->info.execute.recompile;
    }

  /* set host variable values in new session */
  assert (session->parser->flag.set_host_var == 1);
  err = do_set_user_host_variables (new_session, statement->info.execute.using_list);
  if (err != NO_ERROR)
    {
      return err;
    }
  new_session->parser->flag.set_host_var = 0;
  idx = db_compile_statement (new_session);
  if (idx < 0)
    {
      assert (er_errid () != NO_ERROR);
      return er_errid ();
    }

  if (new_session->parser->flag.set_host_var == 0)
    {
      /* Cast host variable to expected domain, if not already casted in db_compile_statement. */
      err = do_cast_host_variables_to_expected_domain (new_session);
      if (err != NO_ERROR)
    {
      return err;
    }
    }

  new_session->parser->flag.is_holdable = session->parser->flag.is_holdable;
  new_session->parser->flag.is_auto_commit = session->parser->flag.is_auto_commit;
  return db_execute_and_keep_statement_local (new_session, 1, result);
}

/*
 * do_process_deallocate_prepare () - deallocate a prepared statement
 * return:   error code or NO_ERROR
 * session (in)   : client session context
 * statement (in) : statement to be deallocated
 */
static int
do_process_deallocate_prepare (DB_SESSION * session, PT_NODE * statement)
{
  const char *const name = statement->info.prepare.name->info.name.original;
  return csession_delete_prepared_statement (name);
}

/*
 * is_allowed_as_prepared_statement () - check if node type is a valid
 *                   prepared statement
 * return:    true if node is valid prepared statement, false otherwise
 *  node (in): parse tree node to check
 */
static bool
is_allowed_as_prepared_statement (PT_NODE * node)
{
  assert (node);

  switch (node->node_type)
    {
    case PT_PREPARE_STATEMENT:
    case PT_EXECUTE_PREPARE:
    case PT_DEALLOCATE_PREPARE:
      return false;

    default:
      return true;
    }
}

/*
 * is_allowed_as_prepared_statement_with_hv () - check if node type is a valid
 *                           prepared statement that can
 *                       accept hostvars
 * return:    true if node is valid prepared statement, false otherwise
 *  node (in): parse tree node to check
 */
static bool
is_allowed_as_prepared_statement_with_hv (PT_NODE * node)
{
  assert (node);

  switch (node->node_type)
    {
    case PT_SELECT:
    case PT_UNION:
    case PT_DIFFERENCE:
    case PT_INTERSECTION:

    case PT_INSERT:
    case PT_UPDATE:
    case PT_DELETE:
    case PT_MERGE:

    case PT_DO:
    case PT_METHOD_CALL:
    case PT_SET_SESSION_VARIABLES:
    case PT_EVALUATE:
      return true;

    case PT_CREATE_ENTITY:
      return (node->info.create_entity.entity_type == PT_CLASS);

    default:
      return false;
    }
}


/*
 * db_has_modified_class()
 *
 *   return:
 *   session(in):
 *   stmt_id(in):
 */
DB_CLASS_MODIFICATION_STATUS
db_has_modified_class (DB_SESSION * session, int stmt_id)
{
  DB_CLASS_MODIFICATION_STATUS cls_status;
  PT_NODE *statement;

  assert (session != NULL);
  assert (stmt_id < session->dimension);

  cls_status = DB_CLASS_NOT_MODIFIED;
  if (stmt_id < session->dimension)
    {
      statement = session->statements[stmt_id];
      if (statement != NULL)
    {
      cls_status = pt_has_modified_class (session->parser, statement);
    }
    }

  return cls_status;
}

/*
 * db_execute_and_keep_statement() - Please refer to the
 *         db_execute_and_keep_statement_local() function
 * return : error status, if execution failed
 *          number of affected objects, if a success & stmt is a SELECT,
 *          UPDATE, DELETE, or INSERT
 * session(in) : contains the SQL query that has been compiled
 * stmt(in) : int returned by a successful compilation
 * result(out): query results descriptor
 */
int
db_execute_and_keep_statement (DB_SESSION * session, int stmt_ndx, DB_QUERY_RESULT ** result)
{
  int err;

  CHECK_CONNECT_MINUSONE ();

  db_invalidate_mvcc_snapshot_before_statement ();

  err = db_execute_and_keep_statement_local (session, stmt_ndx, result);

  db_set_read_fetch_instance_version (LC_FETCH_MVCC_VERSION);

  return err;
}

/*
 * db_execute_statement_local() - This function executes the SQL statement
 *    identified by the stmt argument and returns the result. The
 *    statement ID must have already been returned by a previously successful
 *    call to the db_compile_statement() function.
 * returns  : error status, if execution failed
 *            number of affected objects, if a success & stmt is a
 *            SELECT, UPDATE, DELETE, or INSERT
 * session(in) : contains the SQL query that has been compiled
 * stmt(in) : int returned by a successful compilation
 * result(out): query results descriptor
 *
 * note : You must free the results of calling this function by using the
 *    db_query_end() function. The resources for the identified compiled
 *    statement (not its result) are freed. Consequently, the statement may
 *    not be executed again.
 */
int
db_execute_statement_local (DB_SESSION * session, int stmt_ndx, DB_QUERY_RESULT ** result)
{
  int err;
  PT_NODE *statement;

  if (session == NULL)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  err = db_execute_and_keep_statement_local (session, stmt_ndx, result);

  statement = session->statements[stmt_ndx - 1];
  if (statement != NULL)
    {
      /* free XASL_ID allocated by query_prepare() before freeing the statement */
      pt_free_statement_xasl_id (statement);
      parser_free_tree (session->parser, statement);
      session->statements[stmt_ndx - 1] = NULL;
    }

  return err;
}

/*
 * db_execute_statement() - Please refer to the
 *    db_execute_statement_local() function
 * returns  : error status, if execution failed
 *            number of affected objects, if a success & stmt is a
 *            SELECT, UPDATE, DELETE, or INSERT
 * session(in) : contains the SQL query that has been compiled
 * stmt(in) : int returned by a successful compilation
 * result(out): query results descriptor
 *
 * NOTE: db_execute_statement should be used only as entry point for statement
 *   execution. Otherwise, db_execute_statement_local should be used.
 */
int
db_execute_statement (DB_SESSION * session, int stmt_ndx, DB_QUERY_RESULT ** result)
{
  int err;

  CHECK_CONNECT_MINUSONE ();

  db_invalidate_mvcc_snapshot_before_statement ();

  err = db_execute_statement_local (session, stmt_ndx, result);

  db_set_read_fetch_instance_version (LC_FETCH_MVCC_VERSION);

  return err;
}

/*
 * db_open_buffer_and_compile_first_statement () - The function will open
 *                         buffer for SQL query string
 *                         and will compile the first
 *                         statement in the list.
 *
 * return        : Error code.
 * CSQL_query (in)   : SQL query string.
 * query_error (in)  : Saved query error for output.
 * include_oid (in)  : Include OID mode.
 * session (out)     : Generated session.
 * stmt_no (out)     : Compiled statement number.
 */
int
db_open_buffer_and_compile_first_statement (const char *CSQL_query, DB_QUERY_ERROR * query_error, int include_oid,
                        DB_SESSION ** session, int *stmt_no)
{
  int error = NO_ERROR;
  DB_SESSION_ERROR *errs;

  CHECK_CONNECT_ERROR ();

  /* Open buffer and generate session */
  *session = db_open_buffer_local (CSQL_query);
  if (*session == NULL)
    {
      assert (er_errid () != NO_ERROR);
      return (er_errid ());
    }

  /* Compile the statement */
  db_include_oid (*session, include_oid);
  *stmt_no = db_compile_statement_local (*session);

  errs = db_get_errors (*session);
  if (errs != NULL)
    {
      int line, col;

      (void) db_get_next_error (errs, &line, &col);

      assert (er_errid () != NO_ERROR);
      error = er_errid ();
      if (query_error)
    {
      query_error->err_lineno = line;
      query_error->err_posno = col;
    }
    }

  if (*stmt_no < 0 || error < 0)
    {
      db_close_session_local (*session);
      *session = NULL;
      assert (er_errid () != NO_ERROR);
      return (er_errid ());
    }

  return error;
}

/*
 * db_compile_and_execute_local () - Default compile and execute for local
 *                   calls. See description for
 *                   db_compile_and_execute_queries_internal.
 *
 * return        : Error code.
 * CSQL_query (in)   : SQL query string.
 * result (out)      : Statements results.
 * query_error (out) : Saved query error for output.
 *
 * NOTE: Do not call this function as statement execution entry point. It is
 *   targeted for internal execution calls only!.
 */
int
db_compile_and_execute_local (const char *CSQL_query, void *result, DB_QUERY_ERROR * query_error)
{
  /* Default local compile & execute statements will use: - No oids for include OID mode. - True for execution, not
   * compile only. - Synchronous execution. - This is called during other statement execution, so false for new
   * statements. */
  return db_compile_and_execute_queries_internal (CSQL_query, result, query_error, DB_NO_OIDS, 1, false);
}

/*
 * db_compile_and_execute_queries_internal () - Compiles CSQL_query, executes
 *                      all statements and returns
 *                      results.
 *
 * return        : Error code.
 * CSQL_query (in)   : SQL query string.
 * result (out)      : Statements results.
 * query_error (out)     : Saved query error for output.
 * include_oid (in)  : Include OID mode.
 * execute (in)      : True if query should also be executed. If argument
 *             is false, it will only be compiled.
 * is_new_statement (in) : True these are new statements. If false they are
 *             considered as sub-execution for another statement.
 *
 * NOTE: If executed statements are not part of another statement execution,
 *   before compiling each statement, the snapshot for current transaction
 *   must be invalidated.
 */
int
db_compile_and_execute_queries_internal (const char *CSQL_query, void *result, DB_QUERY_ERROR * query_error,
                     int include_oid, int execute, bool is_new_statement)
{
  int error;            /* return code from funcs */
  int stmt_no;          /* compiled stmt number */
  DB_SESSION *session = NULL;

  if (result)
    {
      /* Initialize result */
      *(char **) result = NULL;
    }

  if (is_new_statement)
    {
      /* invalidate snapshot before compile/execution take place */
      db_invalidate_mvcc_snapshot_before_statement ();
      db_set_read_fetch_instance_version (LC_FETCH_MVCC_VERSION);
    }

  /* Open buffer and compile first statement */
  error = db_open_buffer_and_compile_first_statement (CSQL_query, query_error, include_oid, &session, &stmt_no);
  if (session == NULL)
    {
      /* In case of error, the session is freed */
      return error;
    }

  if (execute)
    {
      /* Execute query and compile next one as long as there are statements left. */
      while (stmt_no > 0)
    {
      /* Execute current query */
      error = db_execute_statement_local (session, stmt_no, (DB_QUERY_RESULT **) result);
      if (error < 0)
        {
          break;
        }
      /* Need to compile and execute next query. Make sure that current MVCC Snapshot is invalidated for READ
       * COMMITTED isolation. */
      if (is_new_statement)
        {
          db_invalidate_mvcc_snapshot_before_statement ();

          db_set_read_fetch_instance_version (LC_FETCH_MVCC_VERSION);
        }
      /* Compile a new statement */
      stmt_no = db_compile_statement_local (session);
    }
    }
  else if (result)
    {
      /* Save query types as result */
      *(DB_QUERY_TYPE **) result = db_get_query_type_list (session, stmt_no);
      if (is_new_statement)
    {
      db_invalidate_mvcc_snapshot_before_statement ();

      db_set_read_fetch_instance_version (LC_FETCH_MVCC_VERSION);
    }
    }

  db_close_session_local (session);

  return error;
}

/*
 * db_set_system_generated_statement () -
 *
 * returns  : error status, if an invalid session is given
 *            NO_ERROR
 * session(in) : contains the SQL query that has been compiled
 *
 */
int
db_set_system_generated_statement (DB_SESSION * session)
{
  CHECK_CONNECT_MINUSONE ();

  if (session == NULL || session->parser == NULL)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      return ER_OBJ_INVALID_ARGUMENTS;
    }

  session->parser->flag.is_system_generated_stmt = 1;

  return NO_ERROR;
}

/*
 * db_drop_statement() - This function frees the resources allocated to a
 *    compiled statement
 * return : void
 * session(in) : session handle
 * stmt(in) : statement id returned by a successful compilation
 */
void
db_drop_statement (DB_SESSION * session, int stmt)
{
  PT_NODE *statement;

  statement = session->statements[stmt - 1];
  if (statement != NULL)
    {
      pt_free_statement_xasl_id (statement);
      parser_free_tree (session->parser, statement);
      session->statements[stmt - 1] = NULL;
      session->stage[stmt - 1] = StatementInitialStage;
    }
}

/*
 * db_drop_all_statements() - This function frees the resources allocated
 *    to a session's compiled statements
 * rerutn : void
 * session(in) : session handle contains the SQL queries that have been
 *   compiled
 */
void
db_drop_all_statements (DB_SESSION * session)
{
  PT_NODE *statement;
  int stmt;

  for (stmt = 0; stmt < session->dimension; stmt++)
    {
      statement = session->statements[stmt];
      if (statement != NULL)
    {
      pt_free_statement_xasl_id (statement);
      parser_free_tree (session->parser, statement);
      session->statements[stmt] = NULL;
      session->stage[stmt] = StatementInitialStage;
    }
    }
  session->dimension = session->stmt_ndx = 0;
}

/*
 * db_close_session_local() - This function frees all resources of this session
 *    except query results
 * return : void
 * session(in) : session handle
 */
void
db_close_session_local (DB_SESSION * session)
{
  PARSER_CONTEXT *parser;
  DB_SESSION *prepared;
  int i;

  if (!session)
    {
      return;
    }
  prepared = session->next;
  while (prepared)
    {
      DB_SESSION *next = prepared->next;
      assert (prepared->is_subsession_for_prepared);
      prepared->next = NULL;
      db_close_session_local (prepared);
      prepared = next;
    }
  parser = session->parser;
  for (i = 0; i < session->dimension; i++)
    {
      PT_NODE *statement;
      if (session->type_list && session->type_list[i])
    {
      db_free_query_format (session->type_list[i]);
    }
      if (session->statements)
    {
      statement = session->statements[i];
      if (statement != NULL)
        {
          pt_free_statement_xasl_id (statement);
          parser_free_tree (parser, statement);
          session->statements[i] = NULL;
        }
    }
    }

  session->dimension = session->stmt_ndx = 0;
  if (session->type_list)
    {
      free_and_init (session->type_list);   /* see db_compile_statement_local() */
    }

  if (parser->host_variables)
    {
      DB_VALUE *hv;

      for (i = 0, hv = parser->host_variables; i < parser->host_var_count + parser->auto_param_count; i++, hv++)
    {
      if (hv)
        {
          db_value_clear (hv);
        }
    }
      free_and_init (parser->host_variables);
    }

  if (parser->host_var_expected_domains)
    {
      free_and_init (parser->host_var_expected_domains);
    }

  parser->host_var_count = parser->auto_param_count = 0;

  pt_free_orphans (session->parser);
  parser_free_parser (session->parser);

  free_and_init (session);
}

/*
 * db_close_session() - Please refer to the db_close_session_local() function
 * return: void
 * session(in) : session handle
 */
void
db_close_session (DB_SESSION * session)
{
  db_close_session_local (session);
}


/*
 * db_get_all_chosen_classes() - This function returns list of all classes
 *    that pass a predicate
 * return : list of class objects that pass a given predicate, if all OK,
 *          NULL otherwise.
 * p(in) : a predicate function
 *
 * note    : the caller is responsible for freeing the list with a call to
 *         db_objlist_free.
 *
 */
static DB_OBJLIST *
db_get_all_chosen_classes (int (*p) (MOBJ o))
{
  LIST_MOPS *lmops;
  DB_OBJLIST *objects, *last, *new_;
  int i;

  objects = NULL;
  lmops = NULL;
  if (au_check_user () == NO_ERROR)
    {
      /* make sure we have a user */
      last = NULL;
      lmops = locator_get_all_class_mops (DB_FETCH_READ, p);
      /* probably should make sure we push here because the list could be long */
      if (lmops != NULL)
    {
      for (i = 0; i < lmops->num; i++)
        {
          /* is it necessary to have this check ? */
          if (!WS_IS_DELETED (lmops->mops[i]) && lmops->mops[i] != sm_Root_class_mop)
        {
          /* should have a ext_ append function */
          new_ = ml_ext_alloc_link ();
          if (new_ == NULL)
            {
              goto memory_error;
            }
          new_->op = lmops->mops[i];
          new_->next = NULL;
          if (last != NULL)
            {
              last->next = new_;
            }
          else
            {
              objects = new_;
            }
          last = new_;
        }
        }
      locator_free_list_mops (lmops);
    }
    }
  return (objects);

memory_error:
  if (lmops != NULL)
    {
      locator_free_list_mops (lmops);
    }
  if (objects)
    {
      ml_ext_free (objects);
    }
  return NULL;
}

/*
 * is_vclass_object() -
 * return:
 * class(in) :
 */
static int
is_vclass_object (MOBJ class_)
{
  return sm_get_class_type ((SM_CLASS *) class_) == SM_VCLASS_CT;
}

/*
 * db_get_all_vclasses_on_ldb() - This function returns list of all ldb
 *    virtual classes
 * return : list of all ldb virtual class objects if all OK,
 *      NULL otherwise.
 */
DB_OBJLIST *
db_get_all_vclasses_on_ldb (void)
{
  return NULL;
}

/*
 * db_get_all_vclasses() - This function returns list of all virtual classes
 * returns  : list of all virtual class objects if all OK,
 *        NULL otherwise.
 */
DB_OBJLIST *
db_get_all_vclasses (void)
{
  DB_OBJLIST *retval;

  retval = db_get_all_chosen_classes (is_vclass_object);

  return (retval);
}


/*
 * db_validate_query_spec() - This function checks that a query_spec is
 *    compatible with a given {vclass} object
 * return  : an ER status code if an error was found, NO_ERROR otherwise.
 * vclass(in) : an {vclass} object
 * query_spec(in) : a query specification string
 */
int
db_validate_query_spec (DB_OBJECT * vclass, const char *query_spec)
{
  PARSER_CONTEXT *parser = NULL;
  PT_NODE **spec = NULL;
  int rc = NO_ERROR, is_vclass = 0;
  const char *const vclass_name = db_get_class_name (vclass);

  if (vclass_name == NULL)
    {
      rc = ER_OBJ_INVALID_ARGUMENTS;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, rc, 0);
      return rc;
    }

  is_vclass = db_is_vclass (vclass);
  if (is_vclass < 0)
    {
      return is_vclass;
    }
  if (!is_vclass)
    {
      rc = ER_SM_NOT_A_VIRTUAL_CLASS;
      er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, rc, 1, vclass_name);
      return rc;
    }

  parser = parser_create_parser ();
  if (parser == NULL)
    {
      rc = ER_GENERIC_ERROR;
      return rc;
    }

  spec = parser_parse_string_use_sys_charset (parser, query_spec);
  if (spec != NULL && !pt_has_error (parser))
    {
      rc = pt_validate_query_spec (parser, *spec, vclass);
    }
  else
    {
      pt_report_to_ersys (parser, PT_SYNTAX);
      rc = er_errid ();
    }

  parser_free_parser (parser);

  return rc;
}

/*
 * get_reasonable_predicate() - This function determines if we can compose
 *   any reasonable predicate against this attribute and return that predicate
 * returns: a reasonable predicate against att if one exists, NULL otherwise
 * att(in) : an instance attribute
 * predicate(out) : the buffer to store the predicate
 * predicate_buf_sz(in) : the size of the predicate buffer
 */
static char *
get_reasonable_predicate (DB_ATTRIBUTE * att, char *predicate, int predicate_buf_sz)
{
  const char *att_name, *cond;

  if (!att || db_attribute_is_shared (att) || !(att_name = db_attribute_name (att)))
    {
      return NULL;
    }

  switch (db_attribute_type (att))
    {
    case DB_TYPE_INTEGER:
    case DB_TYPE_FLOAT:
    case DB_TYPE_DOUBLE:
    case DB_TYPE_SHORT:
    case DB_TYPE_BIGINT:
    case DB_TYPE_MONETARY:
      cond = " = 1 ";
      break;

    case DB_TYPE_STRING:
      cond = " = 'x' ";
      break;

    case DB_TYPE_OBJECT:
      cond = " is null ";
      break;

    case DB_TYPE_SET:
    case DB_TYPE_MULTISET:
    case DB_TYPE_SEQUENCE:
      cond = " = {} ";
      break;

    case DB_TYPE_TIME:
      cond = " = '09:30' ";
      break;

    case DB_TYPE_TIMESTAMP:
    case DB_TYPE_TIMESTAMPLTZ:
      cond = " = '10/15/1986 5:45 am' ";
      break;

    case DB_TYPE_TIMESTAMPTZ:
      cond = " = '10/15/1986 5:45 am +00:00' ";
      break;

    case DB_TYPE_DATETIME:
    case DB_TYPE_DATETIMELTZ:
      cond = " = '10/15/1986 5:45:15.135 am' ";
      break;

    case DB_TYPE_DATETIMETZ:
      cond = " = '10/15/1986 5:45:15.135 am +00:00' ";
      break;

    case DB_TYPE_DATE:
      cond = " = '10/15/1986' ";
      break;

    default:
      return NULL;
    }

  assert (predicate != NULL && predicate_buf_sz > 1);
  snprintf (predicate, predicate_buf_sz - 1, "%s%s", att_name, cond);
  return predicate;
}

/*
 * db_validate() - This function checks if a {class|vclass} definition
 *    is reasonable
 * returns  : an ER status code if an error was found, NO_ERROR otherwise.
 * vc(in) : a {class|vclass} object
 */
int
db_validate (DB_OBJECT * vc)
{
  int retval = NO_ERROR;

  CHECK_CONNECT_ERROR ();

  if (!vc)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      retval = er_errid ();
    }
  else
    {
      retval = db_is_any_class (vc);
      if (retval < 0)
    {
      return retval;
    }
      if (!retval)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_NOT_A_CLASS, 0);
      retval = er_errid ();
    }
      else
    {
      DB_QUERY_SPEC *specs;
      const char *s;

      for (specs = db_get_query_specs (vc); specs; specs = db_query_spec_next (specs))
        {
          s = db_query_spec_string (specs);
          if (s)
        {
          retval = db_validate_query_spec (vc, s);
          if (retval < 0)
            {
              break;
            }
        }
        }
    }
    }

  if (retval >= 0)
    {
      DB_QUERY_RESULT *result = NULL;
      DB_ATTRIBUTE *attributes;
      const char *const separator[2] = { " where ", " and " };
      const int separator_len[2] = { (int) strlen (separator[0]), (int) strlen (separator[1]) };
      int separator_idx = 0;
      char buffer[BUF_SIZE], *pred, *bufp, *newbuf;
      int len, tlen, limit = BUF_SIZE;
      char predicate[300];

      sprintf (buffer, "select count(*) from %s", db_get_class_name (vc));
      attributes = db_get_attributes (vc);
      len = (int) strlen (buffer);
      bufp = buffer;

      while (attributes)
    {
      pred = get_reasonable_predicate (attributes, predicate, sizeof (predicate));
      if (pred)
        {
          /* make sure we have enough room in the buffer */
          tlen = (int) (separator_len[separator_idx] + strlen (pred));
          if (len + tlen >= limit)
        {
          /* increase buffer by BUF_SIZE */
          limit += BUF_SIZE;
          newbuf = (char *) malloc (limit * sizeof (char));
          if (newbuf == NULL)
            {
              er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, limit * sizeof (char));
              break;    /* ran out of memory */
            }

          /* copy old buffer into new buffer and switch */
          strcpy (newbuf, bufp);
          if (bufp != buffer)
            {
              free_and_init (bufp);
            }
          bufp = newbuf;
        }
          /* append another predicate */
          sprintf (bufp + len, "%s%s", separator[separator_idx], pred);
          len += tlen;
          if (separator_idx == 0)
        {
          separator_idx++;
        }
        }
      attributes = db_attribute_next (attributes);
    }

      retval = db_compile_and_execute_local (bufp, &result, NULL);
      if (result)
    {
      db_query_end (result);
    }
      if (bufp != buffer)
    {
      free_and_init (bufp);
    }
    }

  return retval;
}

/*
 * db_free_query() - If an implicit query was executed, free the query on the
 *   server.
 * returns  : void
 * session(in) : session handle
 */
void
db_free_query (DB_SESSION * session)
{
  pt_end_query (session->parser, NULL_QUERY_ID);
}

/*
 * db_check_single_query() - This function checks to see if there is only
 *    one statement given, and that it is a valid query statement.
 * return : error code
 * session(in) : session handle
 */
int
db_check_single_query (DB_SESSION * session)
{
  if (session->dimension > 1)
    {
      return ER_IT_MULTIPLE_STATEMENT;
    }

  return NO_ERROR;
}

#if !defined (SERVER_MODE)
/*
 * db_get_parser() - This function returns session's parser
 * returns: session->parser
 * session (in): session handle
 *
 * note : This is a debugging function.
 */
PARSER_CONTEXT *
db_get_parser (DB_SESSION * session)
{
  return session->parser;
}
#endif /* !defined (SERVER_MODE) */

/*
 * db_get_statement() - This function returns session's statement for id
 * arguments: session (IN): compilation session
 * returns: PT_NODE
 *
 * note : This is a debugging function only.
 *
 */
DB_NODE *
db_get_statement (DB_SESSION * session, int id)
{
  return session->statements[id];
}

/*
 * db_get_parameters() - This function returns a list of the parameters in the
 *    specified statement. There is no implied ordering in the returned list
 *    Parameter names appear once in the returned list, even if they appear
 *    more than once in the statement.
 * return: DB_PARAMETER iterator if there were any parameters in the statement
 * session(in): session handle
 * statement(in): statement number
 */
DB_PARAMETER *
db_get_parameters (DB_SESSION * session, int statement_id)
{
  DB_PARAMETER *result = NULL;
  DB_NODE *statement;

  if (!session || !session->parser || !session->statements || statement_id < 1 || statement_id > session->dimension
      || !(statement = session->statements[statement_id - 1]) || pt_has_error (session->parser))
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      result = NULL;
    }
  else
    {
      result = pt_get_parameters (session->parser, statement);
    }

  return result;
}

/*
 * db_parameter_next() - This function returns the next parameter in a
 *    parameter list or NULL if at the end of the parameter list. The
 *    value given for param must not be NULL Returns the next parameter
 *    in a parameter list or NULL if at the end of the list.
 * return : next parameter in a parameter list
 * param(in) : a parameter
 */
DB_PARAMETER *
db_parameter_next (DB_PARAMETER * param)
{
  DB_PARAMETER *result = NULL;

  if (param)
    {
      result = pt_node_next (param);
    }

  return result;
}

/*
 * db_parameter_name() - This function returns the name for the given
 *    parameter. param must not be a NULL value.
 * return : parameter name
 * param(in) : a parameter
 */
const char *
db_parameter_name (DB_PARAMETER * param)
{
  const char *result = NULL;

  if (param)
    {
      result = pt_string_part (param);
    }

  return result;
}

/*
 * db_bind_parameter_name() -
 * return: error code
 * name(in) : parameter name
 * value(in) : value to be associated
 *
 * note : This function is analogous to other database vendors' use of the
 *        term bind in that it is an association with a variable location.
 */
int
db_bind_parameter_name (const char *name, DB_VALUE * value)
{
  return pt_associate_label_with_value_check_reference (name, value);
}

/*
 * db_query_produce_updatable_result() -
 * return:
 * session(in) :
 * stmt_ndx(in) :
 */
int
db_query_produce_updatable_result (DB_SESSION * session, int stmt_ndx)
{
  PT_NODE *statement;

  /* obvious error checking - invalid parameter */
  if (!session || !session->parser)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_INVALID_SESSION, 0);
      return er_errid ();
    }
  /* no statement was given in the session */
  if (session->dimension == 0 || !session->statements)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_EMPTY_STATEMENT, 0);
      return er_errid ();
    }
  /* invalid parameter */
  statement = session->statements[--stmt_ndx];
  if (stmt_ndx < 0 || stmt_ndx >= session->dimension || !statement)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      return er_errid ();
    }
  /* check if the statement is compiled and prepared */
  if (session->stage[stmt_ndx] < StatementPreparedStage)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_INVALID_SESSION, 0);
      return er_errid ();
    }

  if (statement->node_type == PT_SELECT || statement->node_type == PT_UNION)
    {
      return statement->info.query.oids_included;
    }
  else
    {
      return false;
    }
}

/*
 * db_invalidate_mvcc_snapshot_before_statement () - When MVCC is enabled,
 *                          server uses a snapshot to
 *                          filter data. Snapshot is
 *                          obtained with the first
 *                          fetch or execution on
 *                          server and should be
 *                          invalidated before
 *                          executing a new statement.
 *
 * return : Void.
 *
 * NOTE: When Repeatable Reads and Serializable Isolation are implemented for
 *   MVCC, snapshot must be invalidated only on commit/rollback.
 */
void
db_invalidate_mvcc_snapshot_before_statement (void)
{
  if (TM_TRAN_ISOLATION () >= TRAN_REPEATABLE_READ)
    {
      /* Do not invalidate snapshot after each statement */
      return;
    }

  /* set value of tm_Tran_invalidate_snapshot to 1 in order to invalidate the snapshot next time when we go to server. */

  tm_Tran_invalidate_snapshot = 1;

  /* Increment snapshot version in work space */
  ws_increment_mvcc_snapshot_version ();
}

/*
 * db_set_read_fetch_instance_version () - Set read fetch instance version
 *
 * return : Void.
 * read_Fetch_Instance_Version(in): read fetch instance version to set
 */
void
db_set_read_fetch_instance_version (LC_FETCH_VERSION_TYPE read_Fetch_Instance_Version)
{
  tm_Tran_read_fetch_instance_version = read_Fetch_Instance_Version;
}

/*
 * pt_has_modified_class -
 *   return:
 *
 *   parser(in):
 *   statement(in/out):
 */
static DB_CLASS_MODIFICATION_STATUS
pt_has_modified_class (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  DB_CLASS_MODIFICATION_STATUS status = DB_CLASS_NOT_MODIFIED;

  parser_walk_tree (parser, statement, pt_has_modified_class_helper, &status, NULL, NULL);

  return status;
}

/*
 * pt_has_modified_class_helper -
 *   return:
 *
 *   parser(in):
 *   node(in/out):
 *   arg(in/out):
 *   continue_walk(in/out):
 */
static PT_NODE *
pt_has_modified_class_helper (PARSER_CONTEXT * parser, PT_NODE * node, void *arg, int *continue_walk)
{
  DB_CLASS_MODIFICATION_STATUS *status = (DB_CLASS_MODIFICATION_STATUS *) arg;
  PT_NODE *class_;
  MOP clsmop = NULL;
  SM_CLASS *sm_class = NULL;
  int error = NO_ERROR;

  if (*status != DB_CLASS_NOT_MODIFIED)
    {
      *continue_walk = PT_STOP_WALK;
      return node;
    }

  *continue_walk = PT_CONTINUE_WALK;
  if (node->node_type == PT_SPEC)
    {
      for (class_ = node->info.spec.flat_entity_list; class_; class_ = class_->next)
    {
      clsmop = class_->info.name.db_object;

      if (clsmop == NULL)
        {
          continue;
        }

      if (clsmop->decached)
        {
          /* the class might be aborted. */
          *status = DB_CLASS_MODIFIED;
        }
      else
        {
          error = au_fetch_class_force (clsmop, &sm_class, AU_FETCH_READ);
          if (error != NO_ERROR)
        {
          if (error == ER_HEAP_UNKNOWN_OBJECT)
            {
              /* the class might be dropped. */
              *status = DB_CLASS_MODIFIED;
            }
          else
            {
              *status = DB_CLASS_ERROR;
            }
        }
        }
      if (*status != DB_CLASS_NOT_MODIFIED)
        {
          /* don't revisit leaves */
          *continue_walk = PT_STOP_WALK;
          break;
        }

      if (sm_get_class_type (sm_class) != SM_CLASS_CT)
        {
          continue;
        }

      if (class_->info.name.db_object_chn == NULL_CHN)
        {
          class_->info.name.db_object_chn = locator_get_cache_coherency_number (clsmop);
        }
      else if (class_->info.name.db_object_chn != locator_get_cache_coherency_number (clsmop))
        {
          *status = DB_CLASS_MODIFIED;

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

  return node;
}

/*
 * db_set_statement_auto_commit () - Init statement auto commit.
 *
 * return : error code.
 * session(in): compiled session
 * auto_commit(in): true, if auto commit
 *
 *  Note: This function filters out the statements that can't be executed with commit. In order to execute statement
 *      with commit, the following condition must be satisfied: autocommit mode, no other communication with server
 *      is needed after executing query with commit. Thus, if query with commit is executed, the broker can't send
 *      other fetch, flush or requests to server. However, the client can send various requests before executing
 *      query with commit. Considering this, the optimization can't be applied for queries executed broker side
 *      (triggers, views involved), queries having OID included, multi statements. Currently, the optimization
 *      is used for SELECT/UPDATE/DELETE/INSERT. In future, we have to consider optimization for other query type.
 *      Also, additional filters may be added later in this functions.
 */
int
db_set_statement_auto_commit (DB_SESSION * session, bool auto_commit)
{
  PT_NODE *statement;
  int stmt_ndx;
  int error_code;
  bool has_user_trigger;
  int dimension;

  assert (session != NULL);

  /* check parameters */
  if (session->dimension == 0 || session->statements == NULL)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_EMPTY_STATEMENT, 0);
      return er_errid ();
    }

  stmt_ndx = session->stmt_ndx - 1;
  statement = session->statements[stmt_ndx];
  if (stmt_ndx < 0 || stmt_ndx >= session->dimension || statement == NULL)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_OBJ_INVALID_ARGUMENTS, 0);
      return er_errid ();
    }

  /* check if the statement is compiled and prepared */
  if (session->stage[stmt_ndx] < StatementPreparedStage)
    {
      er_set (ER_WARNING_SEVERITY, ARG_FILE_LINE, ER_IT_INVALID_SESSION, 0);
      return er_errid ();
    }

  /* Set parser auto commit. */
  session->parser->flag.is_auto_commit = auto_commit ? 1 : 0;

  /* Init statement auto commit. */
  statement->flag.use_auto_commit = 0;

  if (!auto_commit || !db_session_is_last_statement (session))
    {
      return NO_ERROR;
    }

  if (session->dimension > 1)
    {
      /* Search for select. */
      if (!session->parser->flag.is_holdable)
    {
      /* Check all statements. */
      dimension = session->dimension;
    }
      else
    {
      /* Check all statements, except the last one. */
      dimension = session->dimension - 1;
    }

      for (int i = 0; i < dimension; i++)
    {
      if (session->statements[i] != NULL && PT_IS_QUERY_NODE_TYPE (session->statements[i]->node_type))
        {
          /* Avoid situation when the driver requests data after closing cursors. */
          return NO_ERROR;
        }
    }
    }

  /* Check whether statement can use auto commit. */
  error_code = tr_has_user_trigger (&has_user_trigger);
  if (error_code != NO_ERROR)
    {
      return error_code;
    }

  if (has_user_trigger)
    {
      /* Triggers must be executed before commit. Disable optimization. */
      return NO_ERROR;
    }

  if (db_can_execute_statement_with_autocommit (session->parser, statement))
    {
      statement->flag.use_auto_commit = 1;
    }

  return NO_ERROR;
}

/*
 * db_can_execute_statement_with_autocommit () - Check whether the statement can be executed with commit.
 *
 * return : true, if the statement can be executed with commit.
 * parser(in): the parser
 * statement(in): the statement
 *
 */
static bool
db_can_execute_statement_with_autocommit (PARSER_CONTEXT * parser, PT_NODE * statement)
{
  bool has_name_oid = false;
  PT_HINT_ENUM info_hints;
  PT_NODE *arg1, *arg2;
  bool can_execute_statement_with_commit;

  assert (parser != NULL && statement != NULL);

  /* Here you can add more statements, if you think that is safe to execute them with commit.
   * For now, we care about optimizing most common queries.
   */
  can_execute_statement_with_commit = false;

  switch (statement->node_type)
    {
    case PT_SELECT:
      /* Check whether the optimization can be used. Disable it, if several broker/server requests are needed. */
      if (!statement->info.query.oids_included && statement->info.query.into_list == NULL)
    {
      info_hints = (PT_HINT_SELECT_KEY_INFO | PT_HINT_SELECT_PAGE_INFO
            | PT_HINT_SELECT_KEY_INFO | PT_HINT_SELECT_BTREE_NODE_INFO);
      if ((statement->info.query.q.select.hint & info_hints) == 0)
        {
          (void) parser_walk_tree (parser, statement->info.query.q.select.list, pt_has_name_oid,
                       &has_name_oid, NULL, NULL);
          if (!has_name_oid)
        {
          can_execute_statement_with_commit = true;
        }
        }
    }
      break;

    case PT_INSERT:
      /* Do not use optimization in case of insert execution on broker side */
      if (statement->info.insert.execute_with_commit_allowed)
    {
      can_execute_statement_with_commit = true;
    }
      break;

    case PT_UPDATE:
      /* Do not use optimization in case of update execution on broker side */
      if (statement->info.update.execute_with_commit_allowed)
    {
      can_execute_statement_with_commit = true;
    }
      break;

    case PT_DELETE:
      /* Do not use optimization in case of delete execution on broker side */
      if (statement->info.delete_.execute_with_commit_allowed)
    {
      /* If del_stmt_list is not null, we may need several broker/server requests */
      if (statement->info.delete_.del_stmt_list == NULL)
        {
          can_execute_statement_with_commit = true;
        }
    }
      break;

    case PT_MERGE:
      if (statement->info.merge.flags & PT_MERGE_INFO_SERVER_OP)
    {
      can_execute_statement_with_commit = true;
    }
      break;

    case PT_UNION:
    case PT_INTERSECTION:
    case PT_DIFFERENCE:
      arg1 = statement->info.query.q.union_.arg1;
      arg2 = statement->info.query.q.union_.arg2;

      /* At least one argument must be not null to enable the optimization. */
      if (arg1 != NULL)
    {
      if (arg2 != NULL)
        {
          if (db_can_execute_statement_with_autocommit (parser, arg1)
          && db_can_execute_statement_with_autocommit (parser, arg2))
        {
          can_execute_statement_with_commit = true;
        }
        }
      else if (db_can_execute_statement_with_autocommit (parser, arg1))
        {
          can_execute_statement_with_commit = true;
        }
    }
      else if (arg2 != NULL)
    {
      if (db_can_execute_statement_with_autocommit (parser, arg2))
        {
          can_execute_statement_with_commit = true;
        }
    }
      break;

      // TODO - what else? for instance, other dmls, ddls.
    default:
      break;
    }

  return can_execute_statement_with_commit;
}

int
db_get_line_of_statement (DB_SESSION * session, int stmt_id)
{
  assert (session->statements != NULL);

  // Safeguards
  if (stmt_id <= 0 || stmt_id > session->dimension || session->statements == NULL
      || session->statements[stmt_id - 1] == NULL)
    {
      // stmt_id is not valid.
      return -1;
    }

  // Get last statement
  return session->statements[stmt_id - 1]->line_number;
}