Skip to content

File csql.c

File List > cubrid > src > executables > csql.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.
 *
 */

/*
 * csql.c : csql main module
 */

#ident "$Id$"

#include "config.h"

#include <stdio.h>
#include <fcntl.h>
#include <limits.h>
#include <assert.h>
#if defined(WINDOWS)
#include <direct.h>
#include <io.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#else /* !WINDOWS */
#include <sys/time.h>
#include <sys/errno.h>
#include <signal.h>
#include <wctype.h>
#include <editline/readline.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif /* !WINDOWS */

#include "authenticate.h"
#include "csql.h"
#include "filesys.hpp"
#include "filesys_temp.hpp"
#include "system_parameter.h"
#include "message_catalog.h"
#include "porting.h"
#include "release_string.h"
#include "error_manager.h"
#include "language_support.h"
#include "network.h"
#include "schema_manager.h"
#include "optimizer.h"
#include "environment_variable.h"
#include "tcp.h"
#include "db.h"
#include "parser.h"
#include "utility.h"
#include "tsc_timer.h"
#include "dbtype.h"
#include "jsp_cl.h"
#include "db_session.h"
#include "cas_log.h"
#include "ddl_log.h"
#include "network_histogram.hpp"
#include "host_lookup.h"
#include "network_interface_cl.h"
#include "boot_cl.h"

#if defined(WINDOWS)
#include "file_io.h"        /* needed for _wyield() */
#endif /* WINDOWS */

#if defined (SA_MODE)
#include "boot_sr.h"
#include "catalog_class.h"
#endif


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

#define DB_NAME_LEN MAXLOGNAME
#define FREE_MEM(PTR)           \
        do {                    \
          if (PTR) {            \
          free(PTR);            \
          }                     \
        } while (0)

/* input type specification for csql_execute_statements() */
enum
{
  FILE_INPUT = 0,       /* FILE stream */
  STRING_INPUT = 1,     /* null-terminated string */
  EDITOR_INPUT = 2      /* command buffer */
};

/* the return value of csql_do_session_cmd() */
enum
{
  DO_CMD_SUCCESS = 0,
  DO_CMD_FAILURE = 1,
  DO_CMD_CONNECT = 2,
  DO_CMD_EXIT = 3
};

#define CSQL_SESSION_COMMAND_PREFIX(C)  (((C) == ';') || ((C) == '!'))

/* size of input buffer */
#define LINE_BUFFER_SIZE         (4000)
#define COLUMN_WIDTH_INFO_LIST_INIT_SIZE 24
#define NOT_FOUND -1

#if defined (ENABLE_UNUSED_FUNCTION)
#if !defined(WINDOWS)
/* current max keyword is 16 + nul char + 3 for expansion */

static int csql_Keyword_num;
static KEYWORD_RECORD *csql_Keyword_list;
#endif /* !WINDOWS */
#endif /* ENABLE_UNUSED_FUNCTION */

int (*csql_text_utf8_to_console) (const char *, const int, char **, int *) = NULL;

int (*csql_text_console_to_utf8) (const char *, const int, char **, int *) = NULL;

int csql_Row_count;
int csql_Num_failures;
char csql_Db_name[512];

/* command editor lines */
int csql_Line_lwm = -1;

/* default environment command names */
char csql_Print_cmd[PATH_MAX] = "lpr";
char csql_Pager_cmd[PATH_MAX] = "more"; /* PAGER does not work on WINDOWS */
#if defined(WINDOWS)
char csql_Editor_cmd[PATH_MAX] = "notepad";
#else
char csql_Editor_cmd[PATH_MAX] = "vi";
#endif

#if defined(WINDOWS)
char csql_Shell_cmd[PATH_MAX] = "command.com";
#else
char csql_Shell_cmd[PATH_MAX] = "csh";
#endif

char csql_Formatter_cmd[PATH_MAX];

/* tty file stream which is used for conversation with users.
 * In batch mode, this will be set to "/dev/null"
 */
static FILE *csql_Tty_fp = NULL;

/* scratch area to make a message text to be displayed.
 * NOTE: Never put chars more than sizeof(csql_Scratch_text).
 */
char csql_Scratch_text[SCRATCH_TEXT_LEN];

int csql_Error_code = NO_ERROR;

typedef enum
{
  CSQL_PROMPT_DEFAULT,
  CSQL_PROMPT_USER_DEFINED,
  CSQL_PROMPT_USER_DEFINED_INCLUDE_USERNAME
} CSQL_PROMPT_TYPE;
static CSQL_PROMPT_TYPE csql_Prompt_user_defined = CSQL_PROMPT_DEFAULT;
static char csql_Prompt_format[100];
static char csql_Prompt_username[100];
static char csql_Prompt[100];
static char csql_Prompt_offline[101];   //  for clear "-Wformat-truncation=" warning

/*
 * Handles for the various files
 */
FILE *csql_Input_fp = NULL;
FILE *csql_Output_fp = NULL;
FILE *csql_Error_fp = NULL;

/*
 * Global longjmp environment to terminate the csql() interpreter in the
 * event of fatal error.  This should be used rather than calling
 * exit(), primarily for the Windows version of the interpreter.
 *
 * Set csql_Exit_status to the numeric status code to be returned from
 * the csql() function after the longjmp has been performed.
 */
static jmp_buf csql_Exit_env;
static int csql_Exit_status = EXIT_SUCCESS;

/* this is non-zero if there is a dangling connection to a database */
static bool csql_Database_connected = false;

static bool csql_Is_interactive = false;
static bool csql_Is_sigint_caught = false;
static bool csql_Is_echo_on = false;
enum
{ HISTO_OFF, HISTO_ON };
static int csql_Is_histo_on = HISTO_OFF;
static bool csql_Is_time_on = true;

static jmp_buf csql_Jmp_buf;

static CSQL_COLUMN_WIDTH_INFO *csql_column_width_info_list = NULL;
static int csql_column_width_info_list_size = 0;
static int csql_column_width_info_list_index = 0;

static bool csql_Query_trace = false;

#if defined (ENABLE_UNUSED_FUNCTION)
#if !defined(WINDOWS)
static char *csql_keyword_generator (const char *text, int state);
static char **csql_cmd_completion_handler (const char *text, int start, int end);
static void init_readline ();
#endif /* ! WINDOWS */
#endif /* ENABLE_UNUSED_FUNCTION */

static void free_csql_column_width_info_list ();
static int initialize_csql_column_width_info_list ();
static int get_column_name_argument (char **column_name, char **val_str, char *argument);
static void csql_pipe_handler (int sig_no);
static void display_buffer (void);
static void start_csql (CSQL_ARGUMENT * csql_arg);
static void csql_read_file (const char *file_name);
static void csql_write_file (const char *file_name, int append_flag);
static void display_error (DB_SESSION * session, int stmt_start_line_no);
static void free_attr_spec (DB_QUERY_TYPE ** attr_spec);
static void csql_print_database (void);
static void csql_set_sys_param (const char *arg_str);
static void csql_get_sys_param (const char *arg_str);
static void csql_set_plan_dump (const char *arg_str);
static void csql_exit_init (void);
static void csql_exit_cleanup (void);
static void csql_print_buffer (void);
static void csql_change_working_directory (const char *dirname);
static void csql_exit_session (int error, bool exit_flag);

static int csql_execute_statements (const CSQL_ARGUMENT * csql_arg, int type, const void *stream, int line_no);

static char *csql_get_external_command (SESSION_CMD cmd_no);
static int csql_do_session_cmd (char *line_read, CSQL_ARGUMENT * csql_arg);
static void csql_set_trace (const char *arg_str);
static void csql_display_trace (void);
static bool csql_is_auto_commit_requested (const CSQL_ARGUMENT * csql_arg);
static int get_host_ip (unsigned char *ip_addr);
static int csql_connect (char *argument, CSQL_ARGUMENT * csql_arg);

static void csql_set_server_output (CSQL_ARGUMENT * csql_arg, bool server_output);
static void csql_print_server_output (const CSQL_ARGUMENT * csql_arg);
static int csql_execute_query (const char *stmts);

static inline void csql_apply_catalog_rebuild_mode (CSQL_ARGUMENT * csql_arg, int *client_type);
static inline int csql_rebuild_catalog (const CSQL_ARGUMENT * csql_arg);

#if defined (ENABLE_UNUSED_FUNCTION)
#if !defined(WINDOWS)
/*
 * for readline keyword completion
 */
/*
 * csql_keyword_generator()
 *   return: char*
 *   text(in)
 *   state(in)
 */
static char *
csql_keyword_generator (const char *text, int state)
{
  static int list_index, len;

  /* If this is a new word to complete, initialize now.  This includes saving the length of TEXT for efficiency, and
   * initializing the index variable to 0. */
  if (!state)
    {
      list_index = 0;
      len = strlen (text);
    }
  if (len == 0)
    {
      return ((char *) NULL);
    }
  if (csql_Keyword_list == NULL)
    {
      return ((char *) NULL);
    }

  /* Return the next name which partially matches from the keyword list. */
  while (list_index < csql_Keyword_num)
    {
      if (strncasecmp ((csql_Keyword_list + list_index)->keyword, text, len) == 0)
    {
      char *ret_str = strdup ((csql_Keyword_list + list_index)->keyword);
      list_index++;
      return ret_str;
    }

      list_index++;
    }

  /* If no keyword matched, then return NULL. */
  return ((char *) NULL);
}

/*
 * csql_cmd_completion_handler()
 *   return: char**
 *   text(in)
 *   start(in)
 *   end(in)
 */
static char **
csql_cmd_completion_handler (const char *text, int start, int end)
{
  char **matches;

  matches = (char **) NULL;
  matches = completion_matches (text, csql_keyword_generator);
  rl_attempted_completion_over = 1;

  return (matches);
}

/*
 * init_readline() - initialize libedit module
 *   return: none
 */
static void
init_readline ()
{
  rl_attempted_completion_function = csql_cmd_completion_handler;
}
#endif /* !WINDOWS */
#endif /* ENABLE_UNUSED_FUNCTION */

/*
 * free_csql_column_width_info_list() - free csql_column_width_info_list
 * return: void
 */
static void
free_csql_column_width_info_list ()
{
  int i;

  if (csql_column_width_info_list == NULL)
    {
      csql_column_width_info_list_size = 0;
      csql_column_width_info_list_index = 0;

      return;
    }

  for (i = 0; i < csql_column_width_info_list_size; i++)
    {
      if (csql_column_width_info_list[i].name != NULL)
    {
      free_and_init (csql_column_width_info_list[i].name);
    }
    }

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

  csql_column_width_info_list_size = 0;
  csql_column_width_info_list_index = 0;
}

/*
 * initialize_csql_column_width_info_list() - initialize csql_column_width_info_list
 * return: int
 */
static int
initialize_csql_column_width_info_list ()
{
  int i;

  csql_column_width_info_list =
    (CSQL_COLUMN_WIDTH_INFO *) malloc (sizeof (CSQL_COLUMN_WIDTH_INFO) * COLUMN_WIDTH_INFO_LIST_INIT_SIZE);
  if (csql_column_width_info_list == NULL)
    {
      csql_Error_code = CSQL_ERR_NO_MORE_MEMORY;

      return CSQL_FAILURE;
    }

  csql_column_width_info_list_size = COLUMN_WIDTH_INFO_LIST_INIT_SIZE;
  csql_column_width_info_list_index = 0;
  for (i = 0; i < csql_column_width_info_list_size; i++)
    {
      csql_column_width_info_list[i].name = NULL;
      csql_column_width_info_list[i].width = 0;
    }

  return CSQL_SUCCESS;
}

/*
 * csql_display_msg() - displays the given msg to output device
 *   return: none
 *   string(in)
 */
void
csql_display_msg (const char *string)
{
  csql_fputs ("\n", csql_Tty_fp);
  csql_fputs_console_conv (string, csql_Tty_fp);
  csql_fputs ("\n", csql_Tty_fp);
}

/*
 * csql_pipe_handler() generic longjmp'ing signal handler used
 *                   where we need to catch broken pipe.
 *   return: none
 *   sig_no(in)
 */
static void
csql_pipe_handler (int sig_no)
{
  longjmp (csql_Jmp_buf, 1);
}

/*
 * display_buffer() - display command buffer into stdout
 *   return: none
 */
static void
display_buffer (void)
{
  volatile int l = 1;
  FILE *pf;
#if !defined(WINDOWS)
  void (*csql_pipe_save) (int);
#endif /* !WINDOWS */

#if !defined(WINDOWS)
  /* There is no SIGPIPE on WINDOWS */
  csql_pipe_save = os_set_signal_handler (SIGPIPE, &csql_pipe_handler);
#endif /* !WINDOWS */
  if (setjmp (csql_Jmp_buf) == 0)
    {
      char *edit_contents, *p;
      pf = csql_popen (csql_Pager_cmd, csql_Output_fp);

      edit_contents = csql_edit_contents_get ();

      putc ('\n', pf);
      while (edit_contents != NULL && *edit_contents != '\0')
    {
      fprintf (pf, "%4d  ", l++);
      p = strchr (edit_contents, '\n');
      if (p)
        {
          fwrite (edit_contents, 1, p - edit_contents, pf);
          edit_contents = p + 1;
        }
      else
        {
          fwrite (edit_contents, 1, strlen (edit_contents), pf);
          edit_contents = NULL;
        }
      fprintf (pf, "\n");
    }
      putc ('\n', pf);

      csql_pclose (pf, csql_Output_fp);
    }
#if !defined(WINDOWS)
  (void) os_set_signal_handler (SIGPIPE, csql_pipe_save);
#endif /* !WINDOWS */
}

/*
 * change_prompt () - to change the csql prompt displayed to the user. 
 *                    The prompt's appearance is customized using a set of user-defined characters specified in the environment variable CUBRID_CSQL_PROMPT.
 *      
 *   return: none
 *   fmt (in)     : the format string containing the user-defined characters, typically sourced from CUBRID_CSQL_PROMPT.
 *   prompt (out) : the buffer where the newly generated, changed prompt string will be stored.
 *   prompt_size (in) : the maximum length (size) of the prompt buffer.
 *   
 *   Note : user-defined characters (escape sequence)
 *     - \u or \U : replaced with the user name
 *     - \d or \D : replaced with the database name
 *     - \h or \H : replaced with the host name
 */
static void
change_prompt (char *fmt, char *prompt, int prompt_size)
{
  char *user_name = NULL;
  char *database_name = NULL;
  char *host_name = NULL;
  char *pos = prompt;
  int remain = prompt_size - 3; // for prompt delimeter + space + null character
  int len;

  if (remain <= 0)
    {
      return;
    }

  memset (prompt, 0x00, prompt_size);

  for (int i = 0; fmt[i] != '\0' && remain > 0;)
    {
      if (fmt[i] == '\\' && fmt[i + 1] != '\0' && fmt[i + 1] >= 0)
    {
      char next = fmt[i + 1];
      const char *src = NULL;

      if (next == 'u' || next == 'U')
        {
          src = user_name = db_get_user_name ();
          strcpy (csql_Prompt_username, user_name);
          csql_Prompt_user_defined = CSQL_PROMPT_USER_DEFINED_INCLUDE_USERNAME;
        }
      else if (next == 'd' || next == 'D')
        {
          src = database_name = db_get_database_name ();
        }
      else if (next == 'h' || next == 'H')
        {
          src = host_name = db_get_host_connected ();
        }

      if (src)
        {
          len = strlen (src);
        }
      else
        {
          src = &(fmt[i]);
          len = 2;
        }

      if (len > remain)
        {
          len = remain;
        }

      memcpy (pos, src, len);
      pos += len;
      remain -= len;
      i += 2;
    }
      else
    {
      *pos++ = fmt[i];
      remain--;
      i++;
    }
    }

  for (; prompt <= pos && (*pos == '\0' || *pos == ' '); --pos);

  if (*pos != '>')
    {
      memcpy (pos + 1, "> \0", 3);
    }
  else
    {
      memcpy (pos + 1, " \0", 2);
    }

  if (user_name)
    {
      db_string_free (user_name);
    }

  if (database_name)
    {
      db_string_free (database_name);
    }

#if !defined (WINDOW)
  /* check if the prompt contains multi-byte characters */
  for (pos = prompt; *pos != '\0'; pos++)
    {
      if ((signed char) *pos < 0)
    {
      char *locale = setlocale (LC_CTYPE, NULL);
      INTL_CODESET codeset = lang_charset ();
      const char *find1 = NULL, *find2 = NULL;

      /* check of DB codeset */
      if (codeset == INTL_CODESET_UTF8)
        {
          find1 = "utf8";
          find2 = "utf-8";
        }
      else if (codeset == INTL_CODESET_KSC5601_EUC)
        {
          find1 = "euckr";
          find2 = "euc-kr";
        }
      else
        {
          goto multibyte_warning;
        }

      /* veify if match LANG and DB codeset */
      if (strcasestr (locale, find1) != NULL || strcasestr (locale, find2) != NULL)
        {
          return;
        }
      else
        {
          goto multibyte_warning;
        }
    }
    }

  return;

multibyte_warning:

  strcpy (prompt, csql_get_message (CSQL_PROMPT));
  if ((prompt_size - strlen (prompt)) > 1)
    {
      strcat (prompt, " ");
    }
  fprintf (stderr, "Warning: %s\n", csql_get_message (CSQL_E_LANG_TEXT));

  return;
#else
  return;
#endif
}

/*
 * start_csql()
 *   return: none
 *   sql_arg(in/out): CSQL_ARGUMENT structure
 *
 * Note:
 * There are four file pointers associated
 *      stdin     - input source
 *      stdout    - output file stream
 *      stderr    - error message file stream
 *      tty_fp    - conversation terminal file stream.
 *                  either NULL or stderr
 *
 * if -o is given, the output file descriptor is duplicated to STDOU_FILENO.
 * Also, if -i is given, -c is given or stdin is not a tty,
 *      `tty_fp' will be set to NULL. (No conversational messages)
 * Otherwise, `tty_fp' will be set to stderr
 *
 * If `single_line_execution' is true, it attemts to execute as soon as
 * it get a line. There is command buffer associated. This is effective
 * only when INTERACTIVE mode (stdin is tty).
 * If `command' is not NULL, it'll execute the command and exit and
 * `-i' option, preceding pipe (if any), `-s' option had no effect.
 */
static void
start_csql (CSQL_ARGUMENT * csql_arg)
{
  unsigned char line_buf[LINE_BUFFER_SIZE];
  unsigned char utf8_line_buf[INTL_UTF8_MAX_CHAR_SIZE * LINE_BUFFER_SIZE];
  char *line_read = NULL;
  int line_length;
  int line_no;
  char *ptr;            /* loop pointer */
  char *line_read_alloced = NULL;
  bool is_first_read_line = true;
  bool read_whole_line;
  char *prompt;

  /* check in string block or comment block or identifier block */
  bool is_in_block = false;

  logddl_set_commit_mode (csql_is_auto_commit_requested (csql_arg));
  if (csql_arg->column_output && csql_arg->line_output)
    {
      csql_Error_code = CSQL_ERR_INVALID_ARG_COMBINATION;
      goto fatal_error;
    }

  csql_Output_fp = stdout;

  if (csql_arg->out_file_name != NULL)
    {
      csql_Output_fp = fopen (csql_arg->out_file_name, "w");
      if (csql_Output_fp == NULL)
    {
      csql_Error_code = CSQL_ERR_OS_ERROR;
      goto fatal_error;
    }
    }

  if (initialize_csql_column_width_info_list () != CSQL_SUCCESS)
    {
      goto fatal_error;
    }

  /* For batch file input and csql command argument input */
  csql_Tty_fp = NULL;
  if (csql_arg->command)
    {
      /* command input */
      csql_exit_session (csql_execute_statements (csql_arg, STRING_INPUT, csql_arg->command, -1), true);
    }

  if (!csql_Is_interactive && !csql_arg->single_line_execution)
    {
      csql_exit_session (csql_execute_statements (csql_arg, FILE_INPUT, csql_Input_fp, -1), true);
    }

  /* Start interactive conversation or single line execution */
  if (csql_Is_interactive)
    {
      csql_Tty_fp = csql_Error_fp;

      if (lang_charset () == INTL_CODESET_UTF8)
    {
      TEXT_CONVERSION *tc = lang_get_txt_conv ();

      if (tc != NULL)
        {
          csql_text_utf8_to_console = tc->utf8_to_text_func;
          csql_text_console_to_utf8 = tc->text_to_utf8_func;
        }
    }
    }

  /* display product title */
  if (!csql_arg->noprint_entrymsg)
    {
      snprintf (csql_Scratch_text, SCRATCH_TEXT_LEN, csql_get_message (CSQL_INITIAL_CSQL_TITLE), rel_release_string ());
      csql_fputs_console_conv (csql_Scratch_text, csql_Tty_fp);

      snprintf (csql_Scratch_text, SCRATCH_TEXT_LEN, "\n%s\n\n", csql_get_message (CSQL_INITIAL_HELP_MSG));
      csql_fputs_console_conv (csql_Scratch_text, csql_Tty_fp);
    }

#if !defined(WINDOWS)
  if (csql_Is_interactive)
    {
#if defined (ENABLE_UNUSED_FUNCTION)
      init_readline ();
#endif /* ENABLE_UNUSED_FUNCTION */
      stifle_history (prm_get_integer_value (PRM_ID_CSQL_HISTORY_NUM));
      using_history ();
#if defined (ENABLE_UNUSED_FUNCTION)
      csql_Keyword_list = pt_get_keyword_rec (&csql_Keyword_num);
#endif /* ENABLE_UNUSED_FUNCTION */
    }
#endif /* !WINDOWS */

  if (csql_Is_interactive && csql_Prompt_user_defined != CSQL_PROMPT_DEFAULT)
    {
      change_prompt (csql_Prompt_format, csql_Prompt, sizeof (csql_Prompt));
    }

  for (line_no = 1;; line_no++)
    {
      if (db_Connect_status == DB_CONNECTION_STATUS_CONNECTED)
    {
      prompt = csql_Prompt;
    }
      else
    {
      csql_Database_connected = false;
      prompt = csql_Prompt_offline;
    }

      read_whole_line = false;

      memset (line_buf, 0, LINE_BUFFER_SIZE);
      memset (utf8_line_buf, 0, INTL_UTF8_MAX_CHAR_SIZE * LINE_BUFFER_SIZE);

      if (csql_Is_interactive)
    {
      if (csql_Database_connected && csql_Prompt_user_defined == CSQL_PROMPT_USER_DEFINED_INCLUDE_USERNAME)
        {
          char *username = db_get_user_name ();

          if (strcmp (csql_Prompt_username, username))
        {
          change_prompt (csql_Prompt_format, csql_Prompt, sizeof (csql_Prompt));
        }

          if (username)
        {
          db_string_free (username);
        }
        }

#if defined(WINDOWS)
      fputs (prompt, csql_Output_fp);   /* display prompt */
      line_read = fgets ((char *) line_buf, LINE_BUFFER_SIZE, csql_Input_fp);
#else
      if ((line_read = readline (prompt)) != NULL)
        {
          if (line_read_alloced != NULL)
        {
          free (line_read_alloced);
          line_read_alloced = NULL;
        }

          line_read_alloced = line_read;

          /* readline assure whole line user typed is located in returned buffer (but it doesn't contain '\n' in
           * it) */
          read_whole_line = true;
        }
#endif /* WINDOWS */
    }
      else
    {
      /* If input line exeeds LINE_BUFFER_SIZE, line_buf couldn't contain '\n' character in it. So, read_whole_line
       * will be remained as false. */
      line_read = fgets ((char *) line_buf, LINE_BUFFER_SIZE, csql_Input_fp);
    }

      fflush (csql_Output_fp);

      if (line_read == NULL)
    {
      if (errno == EINTR && !feof (csql_Input_fp))
        {
          fprintf (csql_Output_fp, "\n");
          continue;
        }

      /* Normal end condtion (with -i option) */
      if (line_read_alloced != NULL)
        {
          free (line_read_alloced);
          line_read_alloced = NULL;
        }
      csql_edit_contents_finalize ();
      csql_exit_session (0, true);
    }

      line_length = strlen (line_read);

      if (csql_Is_interactive)
    {
      char *utf8_line_read = NULL;

      if (csql_text_console_to_utf8 != NULL)
        {
          int utf8_line_buf_size = sizeof (utf8_line_buf);

          utf8_line_read = (char *) utf8_line_buf;
          if ((*csql_text_console_to_utf8) (line_read, line_length, &utf8_line_read, &utf8_line_buf_size) !=
          NO_ERROR)
        {
          goto error_continue;
        }
          if (utf8_line_read != NULL)
        {
          line_read = utf8_line_read;
          line_length = strlen (line_read);
        }
        }
    }

      /* skip UTF-8 BOM if present */
      if (is_first_read_line && intl_is_bom_magic (line_read, line_length))
    {
      line_read += 3;
      line_length -= 3;
    }
      is_first_read_line = false;

      for (ptr = line_read + line_length - 1; line_length > 0; ptr--)
    {
      if (*ptr == '\n')
        {
          read_whole_line = true;
        }

      if (*ptr == '\n' || *ptr == '\r')
        {
          *ptr = '\0';
          line_length--;
        }
      else
        {
          break;
        }
    }

      if (CSQL_SESSION_COMMAND_PREFIX (line_read[0]) && (csql_Is_interactive || (is_in_block == false)))
    {
      int ret;
      ret = csql_do_session_cmd (line_read, csql_arg);
      if (ret == DO_CMD_EXIT || ret == DO_CMD_CONNECT)
        {
          if (line_read_alloced != NULL)
        {
          free (line_read_alloced);
          line_read_alloced = NULL;
        }
          csql_edit_contents_finalize ();
          if (ret == DO_CMD_EXIT)
        {
          csql_exit_session (0, true);
        }
        }
      else if (ret == DO_CMD_FAILURE)
        {
          goto error_continue;
        }

      if (csql_Is_interactive)
        {
          line_no = 0;
        }
      continue;
    }
      else
    {
      bool csql_execute = false;

      if (csql_edit_contents_append (line_read, read_whole_line) != CSQL_SUCCESS)
        {
          goto error_continue;
        }

      if (feof (csql_Input_fp))
        {
          /* if eof is reached, execute all */
          csql_execute = true;
          is_in_block = false;
        }
      else
        {
          csql_walk_statement (line_read);
          /* because we don't want to execute session commands in string block or comment block or identifier block */
          is_in_block = csql_is_statement_in_block ();

          if (csql_arg->single_line_execution
          /* read_whole_line == false means that fgets couldn't read whole line (exceeds buffer size) so we
           * should concat next line before execute it. */
          && read_whole_line == true && csql_is_statement_complete ())
        {
          /* if eof is not reached, execute it only on single line execution mode with complete statement */
          csql_execute = true;
        }
        }

      if (csql_execute)
        {
          /* single-line-oriented execution */
          csql_execute_statements (csql_arg, EDITOR_INPUT, NULL, line_no);
          csql_edit_contents_clear ();
          if (csql_Is_interactive)
        {
          line_no = 0;
        }
        }
    }

      continue;

    error_continue:

      if (csql_Is_interactive)
    {
      line_no = 0;
    }
      nonscr_display_error (csql_Scratch_text, SCRATCH_TEXT_LEN);
    }

fatal_error:
  csql_edit_contents_finalize ();
  if (histo_is_supported ())
    {
      if (csql_Is_histo_on != HISTO_OFF)
    {
      csql_Is_histo_on = HISTO_OFF;
      histo_stop ();
    }
    }

  db_shutdown ();
  csql_Database_connected = false;
  nonscr_display_error (csql_Scratch_text, SCRATCH_TEXT_LEN);
  csql_exit (EXIT_FAILURE);
}

static char *
csql_get_external_command (SESSION_CMD cmd_no)
{
  switch (cmd_no)
    {
    case S_CMD_SHELL_CMD:
      return csql_Shell_cmd;
    case S_CMD_EDIT_CMD:
      return csql_Editor_cmd;
    case S_CMD_PRINT_CMD:
      return csql_Print_cmd;
    case S_CMD_PAGER_CMD:
      return csql_Pager_cmd;
    case S_CMD_FORMATTER_CMD:
      return csql_Formatter_cmd;
    default:
      assert (false);
      return NULL;
    }
}

static int
csql_do_session_cmd (char *line_read, CSQL_ARGUMENT * csql_arg)
{
  char *ptr;
  char *sess_end = NULL;    /* end pos of session command */
  char sess_end_char = '\0';    /* orginal char in end pos of session command */
  char *sess_cmd;       /* session command pointer */
  char *argument;       /* argument str */
  int cmd_no;           /* session command number */
  char *argument_end = NULL;
  int argument_len = 0;
  int error_code;
#if !defined(WINDOWS)
  HIST_ENTRY *hist_entry;
#endif /* !WINDOWS */

  /* get session command and argument */
  ptr = line_read;
  if (csql_Is_echo_on)
    {
      fprintf (csql_Output_fp, "%s\n", line_read);
    }

  /* 'ptr' points to the prefix char. */
  for (ptr++; *ptr != '\0' && iswspace ((wint_t) (*ptr)); ptr++)
    {
      ;
    }
  sess_cmd = (char *) ptr;
  for (; *ptr != '\0' && !iswspace ((wint_t) (*ptr)); ptr++)
    {
      ;
    }
  if (iswspace ((wint_t) (*ptr)))
    {
      sess_end = ptr;
      sess_end_char = *ptr;
      *ptr++ = '\0';        /* put null-termination */
    }
  else
    {
      sess_end_char = '\0';
    }
  for (; *ptr != '\0' && iswspace ((wint_t) (*ptr)); ptr++)
    {
      ;
    }
  argument = (char *) ptr;

  /* remove the end wide-space of argument */
  argument_len = strlen (argument);
  if (argument_len > 0)
    {
      argument_end = argument + argument_len - 1;
      for (; argument_end != argument && iswspace ((wint_t) (*argument_end)); --argument_end)
    {
      *argument_end = '\0';
    }
    }

  /* Now, `sess_cmd' points to null-terminated session command name and `argument' points to remaining argument (it may
   * be '\0' if not given). */

  if (*sess_cmd == '\0' && csql_arg->single_line_execution == false)
    {
      return DO_CMD_SUCCESS;
    }

  cmd_no = csql_get_session_cmd_no (sess_cmd);
  if (cmd_no == -1)
    {
      return DO_CMD_FAILURE;
    }

  /* restore line_read string */
  if (sess_end != NULL)
    {
      *sess_end = sess_end_char;
    }

#if !defined(WINDOWS)
  if (csql_Is_interactive)
    {
      add_history (line_read);
    }
#endif /* !WINDOWS */

  er_clear ();

  switch ((SESSION_CMD) cmd_no)
    {
      /* File stuffs */

    case S_CMD_READ:        /* read a file */
      csql_read_file (argument);
      break;

    case S_CMD_WRITE:       /* write to a file */
    case S_CMD_APPEND:      /* append to a file */
      csql_write_file (argument, cmd_no == S_CMD_APPEND);
      break;

    case S_CMD_PRINT:
      csql_print_buffer ();
      break;

    case S_CMD_SHELL:       /* invoke shell */
      csql_invoke_system (csql_Shell_cmd);
      csql_fputs ("\n", csql_Tty_fp);
      break;

    case S_CMD_CD:
      csql_change_working_directory (argument);
      break;

    case S_CMD_EXIT:        /* exit */
      return DO_CMD_EXIT;

      /* Edit stuffs */

    case S_CMD_CLEAR:       /* clear editor buffer */
      csql_edit_contents_clear ();
      break;

    case S_CMD_EDIT:        /* invoke system editor */
      if (csql_invoke_system_editor ((argument[0] == '\0') ? NULL : argument) != CSQL_SUCCESS)
    {
      return DO_CMD_FAILURE;
    }
      break;

    case S_CMD_LIST:        /* display buffer */
      display_buffer ();
      break;

      /* Command stuffs */
    case S_CMD_RUN:
      csql_execute_statements (csql_arg, EDITOR_INPUT, NULL, -1);
      break;

    case S_CMD_XRUN:
      csql_execute_statements (csql_arg, EDITOR_INPUT, NULL, -1);
      csql_edit_contents_clear ();
      break;

    case S_CMD_COMMIT:
      if (db_commit_transaction () < 0)
    {
      csql_display_csql_err (0, 0);
      csql_check_server_down ();
    }
      else
    {
      csql_display_msg (csql_get_message (CSQL_STAT_COMMITTED_TEXT));
      logddl_write_tran_str (LOGDDL_TRAN_TYPE_COMMIT);
    }
      break;

    case S_CMD_ROLLBACK:
      if (db_abort_transaction () < 0)
    {
      csql_display_csql_err (0, 0);
      csql_check_server_down ();
    }
      else
    {
      csql_display_msg (csql_get_message (CSQL_STAT_ROLLBACKED_TEXT));
      logddl_write_tran_str (LOGDDL_TRAN_TYPE_ROLLBACK);
    }
      break;

    case S_CMD_AUTOCOMMIT:
      if (!strcasecmp (argument, "on"))
    {
      csql_arg->auto_commit = true;
    }
      else if (!strcasecmp (argument, "off"))
    {
      csql_arg->auto_commit = false;
    }

      fprintf (csql_Output_fp, "AUTOCOMMIT IS %s\n", (csql_is_auto_commit_requested (csql_arg) ? "ON" : "OFF"));

      logddl_set_commit_mode (csql_is_auto_commit_requested (csql_arg));
      break;

    case S_CMD_CHECKPOINT:
      if (csql_arg->sysadm && au_is_dba_group_member (Au_user))
    {
      error_code = db_checkpoint ();
      if (error_code != NO_ERROR)
        {
          csql_display_csql_err (0, 0);
        }
      else
        {
          csql_display_msg (csql_get_message (CSQL_STAT_CHECKPOINT_TEXT));
        }
    }
      else
    {
      fprintf (csql_Output_fp, "Checkpointing is only allowed for" " the csql started with --sysadm\n");
    }
      break;

    case S_CMD_KILLTRAN:
      if (csql_arg->sysadm && au_is_dba_group_member (Au_user))
    {
      csql_killtran ((argument[0] == '\0') ? NULL : argument);
    }
      else
    {
      fprintf (csql_Output_fp, "Killing transaction is only allowed" " for the csql started with --sysadm\n");
    }
      break;

    case S_CMD_RESTART:
      if (csql_Database_connected)
    {
      csql_Database_connected = false;
      db_shutdown ();
    }
      er_init ("./csql.err", ER_NEVER_EXIT);
      if (db_restart_ex (UTIL_CSQL_NAME, csql_arg->db_name, csql_arg->user_name, csql_arg->passwd, NULL,
             db_get_client_type ()) != NO_ERROR)
    {
      csql_Error_code = CSQL_ERR_SQL_ERROR;
      csql_display_csql_err (0, 0);
      csql_check_server_down ();
    }
      else
    {
      if (csql_arg->sysadm && au_is_dba_group_member (Au_user))
        {
          int dummy;
          AU_DISABLE (dummy);
        }
      csql_Database_connected = true;

      if (csql_arg->trigger_action_flag == false)
        {
          db_disable_trigger ();
        }

      csql_display_msg (csql_get_message (CSQL_STAT_RESTART_TEXT));
    }
      break;

      /* Environment stuffs */
    case S_CMD_SHELL_CMD:
    case S_CMD_EDIT_CMD:
    case S_CMD_PRINT_CMD:
    case S_CMD_PAGER_CMD:
    case S_CMD_FORMATTER_CMD:
      if (*argument == '\0')
    {
      fprintf (csql_Output_fp, "\n\t%s\n\n", csql_get_external_command ((SESSION_CMD) cmd_no));
    }
      else
    {
      strncpy (csql_get_external_command ((SESSION_CMD) cmd_no), argument, PATH_MAX - 1);
    }
      break;

    case S_CMD_NOPAGER_CMD:
      csql_Pager_cmd[0] = '\0';
      break;

      /* Help stuffs */
    case S_CMD_HELP:
      csql_help_menu ();
      break;

    case S_CMD_SCHEMA:
      csql_help_schema ((argument[0] == '\0') ? NULL : argument);
      if (csql_is_auto_commit_requested (csql_arg))
    {
      if (db_commit_transaction () < 0)
        {
          csql_display_csql_err (0, 0);
          csql_check_server_down ();
        }
      else
        {
          csql_display_msg (csql_get_message (CSQL_STAT_COMMITTED_TEXT));
        }
    }
      break;

    case S_CMD_TRIGGER:
      csql_help_trigger ((argument[0] == '\0') ? NULL : argument);
      if (csql_is_auto_commit_requested (csql_arg))
    {
      if (db_commit_transaction () < 0)
        {
          csql_display_csql_err (0, 0);
          csql_check_server_down ();
        }
      else
        {
          csql_display_msg (csql_get_message (CSQL_STAT_COMMITTED_TEXT));
        }
    }
      break;

    case S_CMD_INFO:
      csql_help_info ((argument[0] == '\0') ? NULL : argument, csql_is_auto_commit_requested (csql_arg));
      break;

    case S_CMD_DATABASE:
      csql_print_database ();
      break;

    case S_CMD_SET_PARAM:
      csql_set_sys_param (argument);
      break;

    case S_CMD_GET_PARAM:
      csql_get_sys_param (argument);
      break;

    case S_CMD_PLAN_DUMP:
      csql_set_plan_dump ((argument[0] == '\0') ? NULL : argument);
      break;

    case S_CMD_ECHO:
      if (!strcasecmp (argument, "on"))
    {
      csql_Is_echo_on = true;
    }
      else if (!strcasecmp (argument, "off"))
    {
      csql_Is_echo_on = false;
    }
      else
    {
      fprintf (csql_Output_fp, "ECHO IS %s\n", (csql_Is_echo_on ? "ON" : "OFF"));
    }
      break;

    case S_CMD_DATE:
      {
    time_t tloc = time (NULL);
    struct tm tmloc;
    char str[80];
    utility_localtime (&tloc, &tmloc);
    strftime (str, 80, "%a %B %d %H:%M:%S %Z %Y", &tmloc);
    fprintf (csql_Output_fp, "\n\t%s\n", str);
      }
      break;

    case S_CMD_TIME:
      if (!strcasecmp (argument, "on"))
    {
      csql_Is_time_on = true;
    }
      else if (!strcasecmp (argument, "off"))
    {
      csql_Is_time_on = false;
    }
      else
    {
      fprintf (csql_Output_fp, "TIME IS %s\n", (csql_Is_time_on ? "ON" : "OFF"));
    }
      break;

    case S_CMD_LINE_OUTPUT:
      if (strcasecmp (argument, "on") == 0)
    {
      if (csql_arg->column_output)
        {
          csql_Error_code = CSQL_ERR_INVALID_ARG_COMBINATION;
          nonscr_display_error (csql_Scratch_text, SCRATCH_TEXT_LEN);
          break;
        }
      csql_arg->line_output = true;
    }
      else if (strcasecmp (argument, "off") == 0)
    {
      csql_arg->line_output = false;
    }
      else
    {
      fprintf (csql_Output_fp, "LINE_OUTPUT IS %s\n", (csql_arg->line_output ? "ON" : "OFF"));
    }
      break;

    case S_CMD_COLUMN_WIDTH:
      {
    char *column_name = NULL;
    int width = 0, result;

    if (*argument == '\0')
      {
        int i;

        for (i = 0; i < csql_column_width_info_list_size; i++)
          {
        if (csql_column_width_info_list[i].name == NULL)
          {
            break;
          }

        if (csql_column_width_info_list[i].width <= 0)
          {
            continue;
          }

        fprintf (csql_Output_fp, "COLUMN-WIDTH %s : %d\n", csql_column_width_info_list[i].name,
             csql_column_width_info_list[i].width);
          }
      }
    else
      {
        char *val_str = NULL;

        result = get_column_name_argument (&column_name, &val_str, argument);
        if (result == CSQL_FAILURE)
          {
        fprintf (csql_Error_fp, "ERROR: Column name is too long.\n");
        break;
          }

        if (val_str == NULL)
          {
        width = csql_get_column_width (column_name);
        fprintf (csql_Output_fp, "COLUMN-WIDTH %s : %d\n", column_name, width);
          }
        else
          {
        trim (val_str);
        result = parse_int (&width, val_str, 10);
        if (result != 0 || width < 0)
          {
            fprintf (csql_Error_fp, "ERROR: Invalid argument(%s).\n", val_str);
            break;
          }

        if (csql_set_column_width_info (column_name, width) != CSQL_SUCCESS)
          {
            return DO_CMD_FAILURE;
          }
          }
      }
      }
      break;

    case S_CMD_STRING_WIDTH:
      {
    int string_width = 0, result;

    if (*argument != '\0')
      {
        trim (argument);

        result = parse_int (&string_width, argument, 10);

        if (result != 0 || string_width < 0)
          {
        fprintf (csql_Error_fp, "ERROR: Invalid string-width(%s).\n", argument);
          }

        csql_arg->string_width = string_width;
      }
    else
      {
        fprintf (csql_Output_fp, "STRING-WIDTH : %d\n", csql_arg->string_width);
      }
      }
      break;

    case S_CMD_HISTO:
      if (histo_is_supported ())
    {
      if (!strcasecmp (argument, "on"))
        {
          if (histo_start (false) == NO_ERROR)
        {
          csql_Is_histo_on = HISTO_ON;
        }
          else
        {
          if (er_errid () == ER_AU_DBA_ONLY)
            {
              fprintf (csql_Output_fp, "Histogram is allowed only for DBA\n");
            }
          else
            {
              fprintf (csql_Output_fp, "Error on .hist command\n");
            }
        }
        }
      else if (!strcasecmp (argument, "off"))
        {
          (void) histo_stop ();
          csql_Is_histo_on = HISTO_OFF;
        }
      else
        {
          fprintf (csql_Output_fp, ".hist IS %s\n", (csql_Is_histo_on == HISTO_OFF ? "OFF" : "ON"));
        }
    }
      else
    {
      fprintf (csql_Output_fp,
           "Histogram is possible when the csql started with " "`communication_histogram=yes'\n");
    }
      break;

    case S_CMD_CLR_HISTO:
      if (histo_is_supported ())
    {
      if (csql_Is_histo_on == HISTO_ON)
        {
          histo_clear ();
        }
      else
        {
          fprintf (csql_Output_fp, ".hist IS currently OFF\n");
        }
    }
      else
    {
      fprintf (csql_Output_fp,
           "Histogram on execution statistics " "is only allowed for the csql started "
           "with `communication_histogram=yes'\n");
    }
      break;

    case S_CMD_DUMP_HISTO:
      if (histo_is_supported ())
    {
      if (csql_Is_histo_on == HISTO_ON)
        {
          if (histo_print (csql_Output_fp) != NO_ERROR)
        {
          return DO_CMD_FAILURE;
        }
          fprintf (csql_Output_fp, "\n");
        }
      else
        {
          fprintf (csql_Output_fp, ".hist IS currently OFF\n");
        }
    }
      else
    {
      fprintf (csql_Output_fp,
           "Histogram on execution statistics " "is only allowed for the csql started "
           "with `communication_histogram=yes'\n");
    }
      break;

    case S_CMD_DUMP_CLR_HISTO:
      if (histo_is_supported ())
    {
      if (csql_Is_histo_on == HISTO_ON)
        {
          if (histo_print (csql_Output_fp) != NO_ERROR)
        {
          return DO_CMD_FAILURE;
        }
          histo_clear ();
          fprintf (csql_Output_fp, "\n");
        }
      else
        {
          fprintf (csql_Output_fp, ".hist IS currently OFF\n");
        }
    }
      else
    {
      fprintf (csql_Output_fp,
           "Histogram on execution statistics " "is only allowed for the csql started "
           "with `communication_histogram=yes'\n");
    }
      break;

    case S_CMD_HISTORY_READ:
#if !defined(WINDOWS)
      if (csql_Is_interactive)
    {
      if (argument[0] != '\0')
        {
          int i = atoi (argument);
          if (i > 0)
        {
          HIST_ENTRY *hist;
          hist = history_get (history_base + i - 1);
          if (hist != NULL)
            {
              if (csql_edit_contents_append (hist->line, true) != CSQL_SUCCESS)
            {
              return DO_CMD_FAILURE;
            }
            }
          else
            {
              fprintf (csql_Error_fp, "ERROR: Invalid history number(%s).\n", argument);
            }
        }
          else
        {
          fprintf (csql_Error_fp, "ERROR: Invalid history number\n");
        }
        }
      else
        {
          fprintf (csql_Error_fp, "ERROR: HISTORYRead {history_number}\n");
        }
    }
#else
      if (csql_Is_interactive)
    {
      fprintf (csql_Error_fp,
           "ERROR: Windows do not support HISTORYList" " and HISTORYRead session commands in csql.\n");
    }
#endif /* !WINDOWS */
      break;

    case S_CMD_HISTORY_LIST:
#if !defined(WINDOWS)
      if (csql_Is_interactive)
    {
      for (int i = 0; i < history_length; i++)
        {
          hist_entry = history_get (i + 1);
          if (hist_entry != NULL && hist_entry->line != NULL)
        {
          fprintf (csql_Output_fp, "----< %d >----\n", i + 1);
          fprintf (csql_Output_fp, "%s\n\n", hist_entry->line);
        }
        }
    }
#else
      if (csql_Is_interactive)
    {
      fprintf (csql_Error_fp,
           "ERROR: Windows do not support HISTORYList" " and HISTORYRead session commands in csql.\n");
    }
#endif /* !WINDOWS */
      break;
    case S_CMD_TRACE:
      if (csql_arg->sa_mode == false)
    {
      csql_set_trace ((argument[0] == '\0') ? NULL : argument);
    }
      else
    {
      fprintf (csql_Error_fp, "Auto trace isn't allowed in SA mode.\n");
    }
      break;

    case S_CMD_SINGLELINE:
      if (!strcasecmp (argument, "on"))
    {
      csql_arg->single_line_execution = true;
    }
      else if (!strcasecmp (argument, "off"))
    {
      csql_arg->single_line_execution = false;
    }
      if (csql_Is_interactive)
    {
      fprintf (csql_Output_fp, "SINGLELINE IS %s\n", (csql_arg->single_line_execution ? "ON" : "OFF"));
    }

      break;

    case S_CMD_CONNECT:
      if (csql_arg->sysadm != true)
    {
      error_code = csql_connect ((argument[0] == '\0') ? NULL : argument, csql_arg);
      if (error_code != NO_ERROR)
        {
          if (csql_Error_code == NO_ERROR)
        {
          return DO_CMD_SUCCESS;
        }
          else
        {
          return DO_CMD_FAILURE;
        }
        }

      if (csql_Prompt_user_defined != CSQL_PROMPT_DEFAULT)
        {
          change_prompt (csql_Prompt_format, csql_Prompt, sizeof (csql_Prompt));
        }
    }
      else
    {
      fprintf (csql_Output_fp, "CONNECT session command does not support --sysadm mode\n");
    }
      return DO_CMD_CONNECT;

    case S_CMD_MIDXKEY:
      if (!strcasecmp (argument, "on"))
    {
      csql_arg->midxkey_print = true;
    }
      else if (!strcasecmp (argument, "off"))
    {
      csql_arg->midxkey_print = false;
    }
      if (csql_Is_interactive)
    {
      fprintf (csql_Output_fp, "MIDXKEY IS %s\n", (csql_arg->midxkey_print ? "ON" : "OFF"));
    }
      break;

    case S_CMD_SERVER_OUTPUT:
      if (strcasecmp (argument, "on") == 0)
    {
      csql_set_server_output (csql_arg, true);
    }
      else if (strcasecmp (argument, "off") == 0)
    {
      csql_set_server_output (csql_arg, false);
    }
      fprintf (csql_Output_fp, "SERVER OUTPUT IS %s\n", (csql_arg->pl_server_output ? "ON" : "OFF"));
      break;
    }

  return DO_CMD_SUCCESS;
}

/*
 * csql_set_server_output() - read a file into command editor
 *   return: none
 *   file_name(in): input file name
 */
static void
csql_set_server_output (CSQL_ARGUMENT * csql_arg, bool server_output)
{
  csql_arg->pl_server_output = server_output;
  if (server_output)
    {
      csql_execute_query ("CALL dbms_output.enable (50000);");
    }
  else
    {
      csql_execute_query ("CALL dbms_output.disable ();");
    }
}

/*
 * csql_read_file() - read a file into command editor
 *   return: none
 *   file_name(in): input file name
 */
static void
csql_read_file (const char *file_name)
{
  static char current_file[PATH_MAX] = "";
  char *p, *q;          /* pointer to string */
  FILE *fp = (FILE *) NULL; /* file stream */

  p = csql_get_real_path (file_name);   /* get real path name */

  if (p == NULL || p[0] == '\0')
    {
      /*
       * No filename given; use the last one we were given.  If we've
       * never received one before we have a genuine error.
       */
      if (current_file[0] != '\0')
    {
      p = current_file;
    }
      else
    {
      csql_Error_code = CSQL_ERR_FILE_NAME_MISSED;
      goto error;
    }
    }

  for (q = p; *q != '\0' && !iswspace ((wint_t) (*q)); q++)
    ;

  /* trim trailing blanks */
  for (; *q != '\0' && iswspace ((wint_t) (*q)); q++)
    {
      *q = '\0';
    }

  if (*q != '\0')
    {               /* contains more than one file name */
      csql_Error_code = CSQL_ERR_TOO_MANY_FILE_NAMES;
      goto error;
    }

  fp = fopen (p, "r");
  if (fp == NULL)
    {
      csql_Error_code = CSQL_ERR_OS_ERROR;
      goto error;
    }

  /*
   * We've successfully read the file, so remember its name for
   * subsequent reads.
   */
  strncpy_bufsize (current_file, p);

  if (csql_edit_read_file (fp) == CSQL_FAILURE)
    {
      goto error;
    }

  fclose (fp);

  csql_display_msg (csql_get_message (CSQL_STAT_READ_DONE_TEXT));

  return;

error:
  if (fp != NULL)
    {
      fclose (fp);
    }
  nonscr_display_error (csql_Scratch_text, SCRATCH_TEXT_LEN);
}

/*
 * csql_write_file() - write (or append) the current content of editor into
 *                   user specified file
 *   return: none
 *   file_name(in): output file name
 *   append_flag(in): true if append
 */
static void
csql_write_file (const char *file_name, int append_flag)
{
  static char current_file[PATH_MAX] = "";
  /* the name of the last file written */
  char *p, *q;          /* pointer to string */
  FILE *fp = (FILE *) NULL; /* file stream */

  p = csql_get_real_path (file_name);   /* get real path name */

  if (p == NULL || p[0] == '\0')
    {
      /*
       * No filename given; use the last one we were given.  If we've
       * never received one before we have a genuine error.
       */
      if (current_file[0] != '\0')
    p = current_file;
      else
    {
      csql_Error_code = CSQL_ERR_FILE_NAME_MISSED;
      goto error;
    }
    }

  for (q = p; *q != '\0' && !iswspace ((wint_t) (*q)); q++)
    ;

  /* trim trailing blanks */
  for (; *q != '\0' && iswspace ((wint_t) (*q)); q++)
    {
      *q = '\0';
    }

  if (*q != '\0')
    {               /* contains more than one file name */
      csql_Error_code = CSQL_ERR_TOO_MANY_FILE_NAMES;
      goto error;
    }

  fp = fopen (p, (append_flag) ? "a" : "w");
  if (fp == NULL)
    {
      csql_Error_code = CSQL_ERR_OS_ERROR;
      goto error;
    }

  /*
   * We've successfully opened the file, so remember its name for
   * subsequent writes.
   */
  strncpy_bufsize (current_file, p);

  if (csql_edit_write_file (fp) == CSQL_FAILURE)
    {
      goto error;
    }

  fclose (fp);

  csql_display_msg (csql_get_message (CSQL_STAT_EDITOR_SAVED_TEXT));

  return;

error:
  if (fp != NULL)
    {
      fclose (fp);
    }
  nonscr_display_error (csql_Scratch_text, SCRATCH_TEXT_LEN);
}

/*
 * csql_print_buffer()
 *   return: none
 *
 * Note:
 *   copy command editor buffer into temporary file and
 *   invoke the user preferred print command to print
 */
static void
csql_print_buffer (void)
{
  /* create an unique file in tmp folder and open it */
  auto[filename, fileptr] = filesys::open_temp_file ("csql_");

  if (!fileptr)
    {
      csql_Error_code = CSQL_ERR_OS_ERROR;
      nonscr_display_error (csql_Scratch_text, SCRATCH_TEXT_LEN);
      return;
    }
  filesys::auto_delete_file file_del (filename.c_str ());   //deletes file at scope end
  filesys::auto_close_file file (fileptr);  //closes file at scope end (before the above file deleter); forget about fp from now on

  /* write the content of editor to the temp file */
  if (csql_edit_write_file (file.get ()) == CSQL_FAILURE)
    {
      nonscr_display_error (csql_Scratch_text, SCRATCH_TEXT_LEN);
      return;
    }
  fflush (file.get ());

  /* invoke the print command */
  char *cmd = csql_get_tmp_buf (1 + strlen (csql_Print_cmd) + 3 + filename.size ());
  if (cmd == NULL)
    {
      nonscr_display_error (csql_Scratch_text, SCRATCH_TEXT_LEN);
      return;
    }
  /*
   * Parenthesize the print command and supply its input through stdin,
   * just in case it's a pipe or something odd.
   */
  sprintf (cmd, "(%s) <%s", csql_Print_cmd, filename.c_str ());
  csql_invoke_system (cmd);
  free_and_init (cmd);

  csql_display_msg (csql_get_message (CSQL_STAT_EDITOR_PRINTED_TEXT));
}

/*
 * csql_change_working_directory()
 *   return: none
 *   dirname(in)
 *
 * Note:
 *   cd to the named directory; if dirname is NULL, cd to
 *   the home directory.
 */
static void
csql_change_working_directory (const char *dirname)
{
  const char *msg;
  char buf[100 + PATH_MAX];

  msg = csql_get_message (CSQL_STAT_CD_TEXT);

  dirname = csql_get_real_path (dirname);

  if (dirname == NULL)
    {
#if defined (WINDOWS)
      static char home_path[PATH_MAX];

      sprintf (home_path, "%s%s", getenv ("HOMEDRIVE"), getenv ("HOMEPATH"));
      dirname = home_path;
#else
      dirname = getenv ("HOME");
#endif
    }

  if (dirname == NULL || chdir (dirname) == -1)
    {
      csql_Error_code = CSQL_ERR_OS_ERROR;
      nonscr_display_error (csql_Scratch_text, SCRATCH_TEXT_LEN);
    }
  else
    {
      snprintf (buf, sizeof (buf) - 1, "\n%s %s.\n\n", msg, dirname);
      csql_fputs_console_conv (buf, csql_Tty_fp);
    }
}

/*
 * display_error()
 *   return: none
 *   session(in)
 *   stmt_start_line_no(in)
 */
static void
display_error (DB_SESSION * session, int stmt_start_line_no)
{
  if (csql_Error_code == CSQL_ERR_SQL_ERROR)
    {
      csql_display_session_err (session, stmt_start_line_no);
      csql_check_server_down ();
    }
  else
    {
      nonscr_display_error (csql_Scratch_text, SCRATCH_TEXT_LEN);

      /* let users read this message before the next overwrites */
      sleep (3);
    }
}

// TODO
/*
 * csql_print_server_output()
 *   return: none
 */
static void
csql_print_server_output (const CSQL_ARGUMENT * csql_arg)
{
  int status = 0;

  if (csql_arg->pl_server_output == false)
    {
      return;
    }

  int errors = csql_execute_query ("EVALUATE '' INTO :pl_output_str");
  errors += csql_execute_query ("EVALUATE 1 INTO :pl_output_status");
  if (errors != 0)
    {
      return;
    }

  bool print_header = true;
  do
    {
      errors = csql_execute_query ("CALL dbms_output.get_line (:pl_output_str, :pl_output_status);");
      if (errors != 0)
    {
      break;
    }
      DB_VALUE *status_val = pt_find_value_of_label ("pl_output_status");
      if (status_val)
    {
      status = db_get_int (status_val);
      if (status == 0)
        {
          DB_VALUE *str_val = pt_find_value_of_label ("pl_output_str");
          if (str_val)
        {
          if (print_header)
            {
              print_header = false;
              fprintf (csql_Output_fp, "<DBMS_OUTPUT>\n");
              fprintf (csql_Output_fp, "====\n");
            }
          const char *str = db_get_string (str_val);
          fprintf (csql_Output_fp, "%s\n", str);
        }
          else
        {
          status = 1;
        }
        }
    }
      else
    {
      status = 1;
    }
    }
  while (status == 0);

  if (!print_header)
    {
      // print_header is false if there was at least one server-output line.
      fprintf (csql_Output_fp, "\n");
    }
}

/*
 * csql_execute_statements() - execute statements
 *   return: >0 if some statement failed, zero otherwise
 *   csql_arg(in)
 *   type(in)
 *   stream(in)
 *
 * Note:
 *   If `type' is STRING_INPUT, it regards `stream' points to command string.
 *   If `type' is FILE_INPUT, it regards `stream' points FILE stream of input
 *   If `type' is EDITOR_INPUT, it attempts to get input string from command
 *   buffer.
 */
static int
csql_execute_statements (const CSQL_ARGUMENT * csql_arg, int type, const void *stream, int line_no)
{
  char *stmts = NULL;       /* statements string */
  int num_stmts = 0;        /* # of stmts executed */
  int stmt_start_line_no = 0;   /* starting line no of each stmt */
  DB_SESSION *session = NULL;   /* query compilation session id */
  DB_QUERY_TYPE *attr_spec = NULL;  /* result attribute spec. */
  int total;            /* number of statements to execute */
  bool do_abort_transaction = false;    /* flag for transaction abort */

  csql_Num_failures = 0;
  er_clear ();
  db_set_interrupt (0);

  if (csql_Is_interactive)
    {
      csql_yyset_lineno (1);
    }

  if (type == FILE_INPUT)
    {               /* FILE * input */
      if (!(session = db_open_file ((FILE *) stream)))
    {
      csql_Error_code = CSQL_ERR_SQL_ERROR;
      goto error;
    }
      logddl_set_csql_input_type (CSQL_INPUT_TYPE_FILE);
      logddl_set_load_filename (csql_arg->in_file_name);
    }
  else if (type == STRING_INPUT)
    {               /* string pointer input */
      session = db_open_buffer ((const char *) stream);
      if (!session)
    {
      csql_Error_code = CSQL_ERR_SQL_ERROR;
      goto error;
    }
      if (csql_Is_echo_on)
    {
      fprintf (csql_Output_fp, "%s\n", (char *) stream);
    }
      logddl_set_csql_input_type (CSQL_INPUT_TYPE_STRING);
    }
  else
    {               /* command buffer input */
      stmts = csql_edit_contents_get ();
      session = db_open_buffer (stmts);
      if (!session)
    {
      csql_Error_code = CSQL_ERR_SQL_ERROR;
      goto error;
    }
      if (csql_Is_echo_on)
    {
      fprintf (csql_Output_fp, "%s\n", stmts);
    }
      logddl_set_csql_input_type (CSQL_INPUT_TYPE_EDITOR);
    }

  /*
   * Make sure that there weren't any syntax errors; if there were, the
   * entire concept of "compile next statement" doesn't make sense, and
   * you run the risk of getting stuck in an infinite loop in the
   * following section (especially if the '-e' switch is on).
   */
  if (db_get_errors (session) || er_errid () != NO_ERROR)
    {
      csql_Error_code = CSQL_ERR_SQL_ERROR;
#if !defined(WINDOWS)
      if ((stmts != NULL) && (csql_Is_interactive))
    {
      add_history (stmts);
    }
#endif /* !WINDOWS */
      goto error;
    }
  else
    {
      total = db_statement_count (session);
#if !defined(WINDOWS)
      if ((total >= 1) && (stmts != NULL) && (csql_Is_interactive))
    {
      add_history (stmts);
    }
#endif /* !WINDOWS */

      /* It is assumed we must always enter the for loop below */
      total = MAX (total, 1);
    }

  logddl_set_commit_mode (csql_is_auto_commit_requested (csql_arg));

  /* execute the statements one-by-one */
  for (num_stmts = 0; num_stmts < total; num_stmts++)
    {
      TSC_TICKS start_tick, end_tick, start_commit_tick, end_commit_tick;
      TSCTIMEVAL elapsed_time;

      int stmt_id;
      CUBRID_STMT_TYPE stmt_type;   /* statement type */
      DB_QUERY_RESULT *result = NULL;   /* result pointer */
      int db_error;
      char stmt_msg[LINE_BUFFER_SIZE];

      /* Start the execution of stms */
      stmt_msg[0] = '\0';

      if (csql_Is_time_on)
    {
      tsc_getticks (&start_tick);
    }
      logddl_set_start_time (NULL);

      stmt_id = db_compile_statement (session);

      assert ((stmt_id < 0) || stmt_id == (num_stmts + 1));
      if (session->statements && session->statements[num_stmts])
    {
      int t_type = (CUBRID_STMT_TYPE) db_get_statement_type (session, num_stmts + 1);
      logddl_check_and_set_query_text (session->statements[num_stmts], t_type, session->parser);
    }

      if (stmt_id <= 0)
    {
      if (stmt_id == 0) /* done */
        {
          break;
        }
      /*
       * Transaction should be aborted if an error occurs during
       * compilation on auto commit mode.
       */
      if (csql_is_auto_commit_requested (csql_arg))
        {
          do_abort_transaction = true;
        }

      logddl_set_err_code (db_error_code ());

      /* compilation error */
      csql_Error_code = CSQL_ERR_SQL_ERROR;
      /* Do not continue if there are no statments in the buffer */
      if (csql_arg->continue_on_error && (db_error_code () != ER_IT_EMPTY_STATEMENT))
        {
          display_error (session, 0);
          /* do_abort_transaction() should be called after display_error() because in some cases it deallocates the
           * parser containing the error message */
          if (do_abort_transaction)
        {
          db_abort_transaction ();
          do_abort_transaction = false;
          logddl_set_msg (LOGDDL_MSG_AUTO_ROLLBACK);
        }
          csql_Num_failures += 1;
          logddl_write_end ();
          continue;
        }
      else
        {
          goto error;
        }
    }

      if (line_no == -1)
    {
      stmt_start_line_no = db_get_start_line (session, stmt_id);
    }
      else
    {
      stmt_start_line_no = line_no;
    }
      attr_spec = db_get_query_type_list (session, stmt_id);
      stmt_type = (CUBRID_STMT_TYPE) db_get_statement_type (session, stmt_id);

      if (db_set_statement_auto_commit (session, csql_is_auto_commit_requested (csql_arg)) != NO_ERROR)
    {
      csql_Error_code = CSQL_ERR_SQL_ERROR;
      goto error;
    }

      db_error = db_execute_statement (session, stmt_id, &result);
      if (db_error < 0)
    {
      logddl_set_err_code (db_error_code ());
      csql_Error_code = CSQL_ERR_SQL_ERROR;
      if (csql_is_auto_commit_requested (csql_arg) && stmt_type != CUBRID_STMT_ROLLBACK_WORK)
        {
          do_abort_transaction = true;
        }
      if (csql_arg->continue_on_error)
        {
          display_error (session, stmt_start_line_no);
          if (do_abort_transaction)
        {
          db_abort_transaction ();
          do_abort_transaction = false;
          logddl_set_msg (LOGDDL_MSG_AUTO_ROLLBACK);
        }
          csql_Num_failures += 1;

          free_attr_spec (&attr_spec);
          logddl_write_end ();
          continue;
        }
      goto error;
    }

      if (csql_Is_time_on)
    {
      tsc_getticks (&end_tick);
      tsc_elapsed_time_usec (&elapsed_time, end_tick, start_tick);
    }

      snprintf (stmt_msg, LINE_BUFFER_SIZE, "Execute OK.");
      csql_Row_count = 0;
      switch (stmt_type)
    {
    case CUBRID_STMT_SELECT:
      {
        const char *msg_p;

        csql_results (csql_arg, result, attr_spec, stmt_start_line_no, stmt_type);

        csql_Row_count = db_error;

        msg_p = ((csql_Row_count > 1) ? csql_get_message (CSQL_ROWS) : csql_get_message (CSQL_ROW));
        snprintf (stmt_msg, LINE_BUFFER_SIZE, msg_p, csql_Row_count, "selected");
        break;
      }

    case CUBRID_STMT_CALL:
    case CUBRID_STMT_EVALUATE:
      if (result != NULL)
        {
          // Results can be values other than NULL
          csql_results (csql_arg, result, db_get_query_type_ptr (result), stmt_start_line_no, stmt_type);
        }
      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_GET_STATS:
      if (result != NULL)
        {
          csql_results (csql_arg, result, db_get_query_type_ptr (result), stmt_start_line_no, stmt_type);
        }
      break;

    case CUBRID_STMT_UPDATE:
    case CUBRID_STMT_DELETE:
    case CUBRID_STMT_INSERT:
    case CUBRID_STMT_MERGE:
      {
        const char *msg_p;

        msg_p = ((db_error > 1) ? csql_get_message (CSQL_ROWS) : csql_get_message (CSQL_ROW));
        snprintf (stmt_msg, LINE_BUFFER_SIZE, msg_p, db_error, "affected");
        break;
      }

    case CUBRID_STMT_KILL:
      {
        const char *msg_p;

        msg_p = ((db_error > 1) ? csql_get_message (CSQL_TRANSACTIONS) : csql_get_message (CSQL_TRANSACTION));
        snprintf (stmt_msg, LINE_BUFFER_SIZE, msg_p, db_error, "killed");
        break;
      }

    default:
      break;
    }

      free_attr_spec (&attr_spec);

      if (result != NULL)
    {
      db_query_end (result);
      result = NULL;
    }
      else
    {
      /*
       * Even though there are no results, a query may have been
       * run implicitly by the statement.  If so, we need to end the
       * query on the server.
       */
      db_free_query (session);
    }

      if (csql_Is_time_on)
    {
      char time[100];

      sprintf (time, " (%ld.%06ld sec) ", elapsed_time.tv_sec, elapsed_time.tv_usec);
      strncat (stmt_msg, time, sizeof (stmt_msg) - strlen (stmt_msg) - 1);
    }

      if (csql_is_auto_commit_requested (csql_arg) && stmt_type != CUBRID_STMT_COMMIT_WORK
      && stmt_type != CUBRID_STMT_ROLLBACK_WORK)
    {
      if (csql_Is_time_on)
        {
          tsc_getticks (&start_commit_tick);
        }
      db_error = db_commit_transaction ();
      if (db_error < 0)
        {
          logddl_set_err_code (db_error_code ());
          csql_Error_code = CSQL_ERR_SQL_ERROR;
          do_abort_transaction = true;

          if (csql_arg->continue_on_error)
        {
          display_error (session, stmt_start_line_no);
          if (do_abort_transaction)
            {
              db_abort_transaction ();
              do_abort_transaction = false;
              logddl_set_msg (LOGDDL_MSG_AUTO_ROLLBACK);
            }
          csql_Num_failures += 1;
          logddl_write_end ();
          continue;
        }
          goto error;
        }
      else
        {
          strncat (stmt_msg, csql_get_message (CSQL_STAT_COMMITTED_TEXT), LINE_BUFFER_SIZE - 1);
          if (csql_Is_time_on)
        {
          char time[100];
          tsc_getticks (&end_commit_tick);
          tsc_elapsed_time_usec (&elapsed_time, end_commit_tick, start_commit_tick);
          sprintf (time, " (%ld.%06ld sec) ", elapsed_time.tv_sec, elapsed_time.tv_usec);
          strncat (stmt_msg, time, sizeof (stmt_msg) - strlen (stmt_msg) - 1);
        }
        }
    }

      if (csql_is_auto_commit_requested (csql_arg) == false && (stmt_type == CUBRID_STMT_COMMIT_WORK
                                || stmt_type == CUBRID_STMT_ROLLBACK_WORK))
    {
      if (stmt_type == CUBRID_STMT_COMMIT_WORK)
        {
          (type !=
           FILE_INPUT) ? logddl_write_tran_str (LOGDDL_TRAN_TYPE_COMMIT) :
    logddl_write_tran_str ("Commit transaction at line %d", stmt_start_line_no);
        }
      else if (stmt_type == CUBRID_STMT_ROLLBACK_WORK)
        {
          (type !=
           FILE_INPUT) ? logddl_write_tran_str (LOGDDL_TRAN_TYPE_ROLLBACK) :
    logddl_write_tran_str ("Rollback transaction at line %d", stmt_start_line_no);
        }
    }

      if (csql_arg->pl_server_output)
    {
      csql_print_server_output (csql_arg);
    }

      if (csql_arg->plain_output == false && csql_arg->query_output == false && csql_arg->loaddb_output == false)
    {
      fprintf (csql_Output_fp, "%s\n", stmt_msg);
    }

      db_drop_statement (session, stmt_id);

      if (type != FILE_INPUT)
    {
      if (csql_is_auto_commit_requested (csql_arg))
        {
          logddl_set_msg (LOGDDL_MSG_AUTO_COMMIT);
        }
      logddl_write_end ();
    }
    }

  if (csql_arg->sysadm_rebuild_catalog)
    {
      if (csql_rebuild_catalog (csql_arg) != NO_ERROR)
    {
      csql_Error_code = CSQL_ERR_SYSTEM_CATALOG_COMPILE;
      goto error;
    }
    }

  snprintf (csql_Scratch_text, SCRATCH_TEXT_LEN, csql_get_message (CSQL_EXECUTE_END_MSG_FORMAT),
        num_stmts - csql_Num_failures);
  csql_display_msg (csql_Scratch_text);

  if (type == FILE_INPUT)
    {
      logddl_write_end_for_csql_fileinput ("Total %8d statements executed. %8d statements failed.", total,
                       csql_Num_failures);
    }

  db_close_session (session);

  if (csql_Query_trace == true)
    {
      csql_display_trace ();
    }

  if (csql_is_auto_commit_requested (csql_arg))
    {
      logddl_free (true);
    }
  return csql_Num_failures;

error:
  if (csql_arg->sysadm_rebuild_catalog)
    {
      do_abort_transaction = true;
    }

  display_error (session, stmt_start_line_no);
  if (csql_arg->pl_server_output)
    {
      csql_print_server_output (csql_arg);
    }
  logddl_set_err_code (db_error_code ());
  if (do_abort_transaction)
    {
      db_abort_transaction ();
      do_abort_transaction = false;
      logddl_set_msg (LOGDDL_MSG_AUTO_ROLLBACK);
    }
  else
    {
      (void) db_reset_latest_query_status ();
    }

  /* Finish... */
  snprintf (csql_Scratch_text, SCRATCH_TEXT_LEN, csql_get_message (CSQL_EXECUTE_END_MSG_FORMAT),
        num_stmts - csql_Num_failures);
  csql_display_msg (csql_Scratch_text);
  (type == FILE_INPUT) ? logddl_write_end_for_csql_fileinput ("") : logddl_write_end ();

  if (session)
    {
      db_close_session (session);
    }

  free_attr_spec (&attr_spec);
  if (csql_is_auto_commit_requested (csql_arg))
    {
      logddl_free (true);
    }
  return 1;
}

/*
 * free_attr_spec()
 *   return: none
 *   attr_spec(in/out)
 *
 * Note: Free memory alloced for attr_spec and set pointer to NULL
 */
static void
free_attr_spec (DB_QUERY_TYPE ** attr_spec)
{
  if (*attr_spec != NULL)
    {
      db_query_format_free (*attr_spec);
      *attr_spec = NULL;
    }
}

/*
 * csql_print_database()
 */
static void
csql_print_database (void)
{
  struct sockaddr_in sin;
  const char *db_name, *host_name;
  char *pstr;
  char converted_host_name[CUB_MAXHOSTNAMELEN + 1];
  char ha_state[16];
  int res;

  db_name = db_get_database_name ();
  host_name = db_get_host_connected ();

  if (db_name == NULL || host_name == NULL)
    {
      fprintf (csql_Error_fp, "\n\tNOT CONNECTED\n\n");
    }
  else
    {
      sin.sin_family = AF_INET;
      sin.sin_addr.s_addr = inet_addr (host_name);

      res =
    getnameinfo_uhost ((struct sockaddr *) &sin, sizeof (sin), converted_host_name, sizeof (converted_host_name),
               NULL, 0, NI_NAMEREQD);
      /*
       * if it fails to resolves hostname,
       * it will use db_get_host_connected()'s result.
       */
      if (res != 0)
    {
      strncpy (converted_host_name, host_name, CUB_MAXHOSTNAMELEN);
      converted_host_name[CUB_MAXHOSTNAMELEN] = '\0';
    }

      if (strcasecmp (converted_host_name, "localhost") == 0
      || strcasecmp (converted_host_name, "localhost.localdomain") == 0)
    {
      if (GETHOSTNAME (converted_host_name, CUB_MAXHOSTNAMELEN) != 0)
        {
          strncpy (converted_host_name, host_name, CUB_MAXHOSTNAMELEN);
        }
    }
      converted_host_name[CUB_MAXHOSTNAMELEN] = '\0';

      /*
       * if there is hostname or ip address in db_name,
       * it will only use db_name except for hostname or ip address.
       */
      pstr = (char *) strchr (db_name, '@');
      if (pstr != NULL)
    {
      *pstr = '\0';
    }

      if (HA_DISABLED ())
    {
      fprintf (csql_Output_fp, "\n\t%s@%s\n\n", db_name, converted_host_name);
    }
      else
    {
      db_get_ha_server_state (ha_state, 16);
      fprintf (csql_Output_fp, "\n\t%s@%s [%s]\n\n", db_name, converted_host_name, ha_state);
    }

      db_ws_free ((char *) db_name);
    }
}

/*
 * csql_set_sys_param()
 *   return: none
 *   arg_str(in)
 *
 * Note: Parse the arg string to find out what system parameter to
 *       clobber, then clobber it.  Originally introduced to allow us
 *       to fiddle with optimizer parameters.
 */
static void
csql_set_sys_param (const char *arg_str)
{
  char plantype[128];
  char val[128];
  char ans[4096];
  int level;
  int len = sizeof (ans);

  if (arg_str == NULL)
    return;

  if (strncmp (arg_str, "cost", 4) == 0 && sscanf (arg_str, "cost %127s %127s", plantype, val) == 2)
    {
      if (qo_plan_set_cost_fn (plantype, val[0]))
    {
      snprintf (ans, len - 1, "cost %s: %s", plantype, val);
    }
      else
    {
      snprintf (ans, len - 1, "error: unknown cost parameter %s", plantype);
    }
    }
  else if (strncmp (arg_str, "level", 5) == 0 && sscanf (arg_str, "level %d", &level) == 1)
    {
      if (CHECK_INVALID_OPTIMIZATION_LEVEL (level))
    {
      snprintf (ans, len - 1, "error: wrong value %d", level);
    }
      else
    {
      qo_set_optimization_param (NULL, QO_PARAM_LEVEL, level);
      strncpy (ans, arg_str, len - 1);
    }
    }
  else
    {
      strncpy (ans, arg_str, len - 1);
      if (db_set_system_parameters (ans) != NO_ERROR)
    {
      snprintf (ans, len, "error: set %s", arg_str);
    }
    }

  csql_append_more_line (0, ans);
  csql_display_more_lines ("Set Param Input");
  csql_free_more_lines ();
}

/*
 * csql_get_sys_param()
 *   return:
 *   arg_str(in)
 */
static void
csql_get_sys_param (const char *arg_str)
{
  char plantype[128];
  int cost;
  char ans[4096];
  int level;
  int len = sizeof (ans);

  if (arg_str == NULL)
    return;

  if (strncmp (arg_str, "cost", 4) == 0 && sscanf (arg_str, "cost %127s", plantype) == 1)
    {
      cost = qo_plan_get_cost_fn (plantype);
      if (cost == 'u')
    {
      snprintf (ans, len, "error: unknown cost parameter %s", arg_str);
    }
      else
    {
      snprintf (ans, len, "cost %s: %c", arg_str, (char) cost);
    }
    }
  else if (strcmp (arg_str, "level") == 0)
    {
      qo_get_optimization_param (&level, QO_PARAM_LEVEL);
      snprintf (ans, len, "level %d", level);
    }
  else
    {
      strncpy (ans, arg_str, len - 1);
      if (db_get_system_parameters (ans, len - 1) != NO_ERROR)
    {
      snprintf (ans, len - 1, "error: get %s", arg_str);
    }
    }

  csql_append_more_line (0, ans);
  csql_display_more_lines ("Get Param Input");
  csql_free_more_lines ();
}

/*
 * csql_set_plan_dump()
 *   return:
 *   arg_str(in)
 */
static void
csql_set_plan_dump (const char *arg_str)
{
  int level;
  char line[128];

  qo_get_optimization_param (&level, QO_PARAM_LEVEL);

  if (arg_str != NULL)
    {
      if (!strncmp (arg_str, "simple", 6))
    {
      level &= ~0x200;
      level |= 0x100;
      qo_set_optimization_param (NULL, QO_PARAM_LEVEL, level);
    }
      else if (!strncmp (arg_str, "detail", 6))
    {
      level &= ~0x100;
      level |= 0x200;
      qo_set_optimization_param (NULL, QO_PARAM_LEVEL, level);
    }
      else if (!strncmp (arg_str, "off", 3))
    {
      level &= ~(0x100 | 0x200);
      qo_set_optimization_param (NULL, QO_PARAM_LEVEL, level);
    }
    }

  if (PLAN_DUMP_ENABLED (level))
    {
      if (SIMPLE_DUMP (level))
    {
      snprintf (line, 128, "plan simple (opt level %d)", level);
    }
      if (DETAILED_DUMP (level))
    {
      snprintf (line, 128, "plan detail (opt level %d)", level);
    }
    }
  else
    {
      snprintf (line, 128, "plan off (opt level %d)", level);
    }

  csql_append_more_line (0, line);
  csql_display_more_lines ("Plan Dump");
  csql_free_more_lines ();
}

/*
 * signal_intr() - Interrupt handler for csql
 *   return: none
 *   sig_no(in)
 */
#if defined(WINDOWS)
static BOOL WINAPI
#else /* !WINDOWS */
static void
#endif              /* WINDOWS */
signal_intr (int sig_no)
{
  if (csql_Is_interactive)
    {
      db_set_interrupt (1);
    }
  csql_Is_sigint_caught = true;

#if defined(WINDOWS)
  if (sig_no == CTRL_C_EVENT)
    {
      return TRUE;
    }

  return FALSE;
#endif /* WINDOWS */
}

/*
 * signal_stop()
 *   return: none
 *   sig_no(in)
 *
 * Note: Interrupt handler for ^Z. This is needed since the terminal
 *       must be changed from raw to cooked. After we return, it must
 *       be set back.
 */
static void
signal_stop (int sig_no)
{
  static int cont = 0;

#if defined(WINDOWS)
  /* there is no SIGSTP on NT */
  cont = 1;
#else /* !WINDOWS */
  if (sig_no == SIGTSTP)
    {
      cont = 0;
      (void) os_set_signal_handler (SIGTSTP, SIG_DFL);

      /* send the signal to ourselves */
      os_send_signal (SIGTSTP);

      /* Wait for SIGCONT */
      while (cont == 0)
    {
      pause ();
    }
      (void) os_set_signal_handler (SIGTSTP, signal_stop);
    }
  else
    {
      /* Continue */
      cont = 1;
    }
#endif /* !WINDOWS */
}

#if !defined(WINDOWS)
/*
 * crash_handler(): kill the server and spawn the new server process
 *
 *   returns: none
 *   signo(IN): signo to handle
 *   siginfo(IN): siginfo struct
 *   dummyp(IN): this argument will not be used,
 *               but remains to cope with its function prototype.
 *
 */

static void
crash_handler (int signo, siginfo_t * siginfo, void *dummyp)
{
  if (os_set_signal_handler (signo, SIG_DFL) == SIG_ERR)
    {
      return;
    }

  er_print_crash_callstack (signo);
}

/*
 * register_crash_signal_handler () : register of the signal for occuring crash
 *
 *   returns : none
 *   signo(IN): signo to handle
 *
 */

static void
register_crash_signal_handler (int signo)
{
  struct sigaction act;

  act.sa_handler = NULL;
  act.sa_sigaction = crash_handler;
  sigemptyset (&act.sa_mask);
  act.sa_flags = 0;
  act.sa_flags |= SA_SIGINFO;
  sigaction (signo, &act, NULL);
}
#endif

/*
 * csql_exit_session() - handling the default action of the last outstanding
 *                     transaction (i.e., commit or abort)
 *   return:  none
 *   error(in)
 *
 * Note: this function never return.
 */
static void
csql_exit_session (int error, bool exit_flag)
{
  char line_buf[LINE_BUFFER_SIZE];
  bool commit_on_shutdown = false;
  bool prm_commit_on_shutdown = prm_get_commit_on_shutdown ();

  free_csql_column_width_info_list ();

  if (!db_commit_is_needed ())
    {
      /* when select statements exist only in session, marks end of transaction to flush audit records for those
       * statements */
      db_abort_transaction ();
    }

  if (csql_Is_interactive && !prm_commit_on_shutdown && db_commit_is_needed () && !feof (csql_Input_fp))
    {
      FILE *tf;

      tf = csql_Error_fp;

      /* interactive, default action is abort but there was update */
      fprintf (tf, csql_get_message (CSQL_TRANS_TERMINATE_PROMPT_TEXT));
      fflush (tf);
      for (; fgets (line_buf, LINE_BUFFER_SIZE, csql_Input_fp) != NULL;)
    {
      if (line_buf[0] == 'y' || line_buf[0] == 'Y')
        {
          commit_on_shutdown = true;
          logddl_write_tran_str (LOGDDL_TRAN_TYPE_COMMIT);
          break;
        }
      if (line_buf[0] == 'n' || line_buf[0] == 'N')
        {
          commit_on_shutdown = false;
          logddl_write_tran_str (LOGDDL_TRAN_TYPE_ROLLBACK);
          break;
        }

      fprintf (tf, csql_get_message (CSQL_TRANS_TERMINATE_PROMPT_RETRY_TEXT));
      fflush (tf);
    }

      if (commit_on_shutdown && db_commit_transaction () < 0)
    {
      nonscr_display_error (csql_Scratch_text, SCRATCH_TEXT_LEN);
      error = 1;
    }
    }

  if (!exit_flag)
    {
      return;
    }

  if (histo_is_supported ())
    {
      if (csql_Is_histo_on != HISTO_OFF)
    {
      csql_Is_histo_on = HISTO_OFF;
      histo_stop ();
    }
    }

  if (db_shutdown () < 0)
    {
      csql_Database_connected = false;
      nonscr_display_error (csql_Scratch_text, SCRATCH_TEXT_LEN);
      csql_exit (EXIT_FAILURE);
    }
  else
    {
      csql_Database_connected = false;
      csql_exit (error ? EXIT_FAILURE : EXIT_SUCCESS);
    }
}

/*
 * csql_exit_init()
 *   return: none
 *
 * Note:
 *    Initialize various state variables we keep to let us know what
 *    cleanup operations need to be performed when the csql() function
 *    exits.  This should properly initialize everything that is tested
 *    by the csql_exit_cleanup function.
 */
static void
csql_exit_init (void)
{
  csql_Exit_status = EXIT_SUCCESS;
  csql_Database_connected = false;

  csql_Input_fp = stdin;
  csql_Output_fp = stdout;
  csql_Error_fp = stderr;
}

/*
 * csql_exit_cleanup()
 *   return: none
 *
 * Note:
 *    Called by csql() when the exit longjmp has been taken.
 *    Examine the various state variables we keep and perform any
 *    termination cleanup operations that need to be performed.
 *    For the Windows implementation, it is especially important that the
 *    csql() function return cleanly.
 */
static void
csql_exit_cleanup ()
{
  FILE *oldout;

  if (csql_Input_fp != NULL && csql_Input_fp != stdin)
    {
      (void) fclose (csql_Input_fp);
      csql_Input_fp = NULL;
    }

  oldout = csql_Output_fp;
  if (csql_Output_fp != NULL && csql_Output_fp != stdout)
    {
      (void) fclose (csql_Output_fp);
      csql_Output_fp = NULL;
    }

  if (csql_Error_fp != NULL && csql_Error_fp != oldout && csql_Error_fp != stdout && csql_Error_fp != stderr)
    {
      (void) fclose (csql_Error_fp);
      csql_Error_fp = NULL;
    }

  if (csql_Database_connected)
    {
      if (histo_is_supported ())
    {
      if (csql_Is_histo_on != HISTO_OFF)
        {
          csql_Is_histo_on = HISTO_OFF;
          histo_stop ();
        }
    }

      csql_Database_connected = false;
      db_shutdown ();
    }

  /* Note that this closes a global resource, the "kernel" message catalog. This is ok for the Unix implementation as
   * the entire process is about to exit.  For the Windows implementation, it happens to be ok since the test driver
   * application that calls csql() won't use this catalog. If this ever changes however, we'll probably have to
   * maintain some sort of internal reference counter on this catalog so that it won't be freed until all the nested
   * users close it. */
  lang_final ();
}

/*
 * csql_exit()
 *   return:  none
 *   exit_status(in)
 * Note:
 *    This should be called rather than exit() any place that the code wants
 *    to terminate the csql interpreter program.  Rather than exit(), it
 *    will longjmp back to the csql() function which will clean up and
 *    return the status code to the calling function.  Usually the calling
 *    function is main() but under Windows, the caller may be a more complex
 *    application.
 */
void
csql_exit (int exit_status)
{
  csql_Exit_status = exit_status;
  longjmp (csql_Exit_env, 1);
}

/* --sysadm-rebuild-catalog option is only supported in SA_MODE */
static inline void
csql_apply_catalog_rebuild_mode (CSQL_ARGUMENT * csql_arg, int *client_type)
{
#if defined (SA_MODE)
  *client_type = DB_CLIENT_TYPE_ADMIN_CSQL_REBUILD_CATALOG;
  csql_arg->sysadm = true;
  csql_arg->auto_commit = false;
  boot_set_skip_check_ct_classes (true);
#endif /* SA_MODE */
}

static int
csql_rebuild_catalog (const CSQL_ARGUMENT * csql_arg)
{
#if defined (SA_MODE)
  assert (catcls_Enable == false);

  if (catcls_compile_catalog_classes (NULL) != NO_ERROR)
    {
      return ER_FAILED;
    }
  if (sm_force_write_all_classes () != NO_ERROR)
    {
      return ER_FAILED;
    }
  if (sm_update_all_catalog_statistics (STATS_WITH_FULLSCAN) != NO_ERROR)
    {
      return ER_FAILED;
    }

  db_commit_transaction ();
#endif /* SA_MODE */
  return NO_ERROR;
}

/*
 * csql() - "main" interface function for the csql interpreter
 *   return: EXIT_SUCCESS, EXIT_FAILURE
 *   csql_arg(in)
 */
int
csql (const char *argv0, CSQL_ARGUMENT * csql_arg)
{
  char *env;
  int client_type;
  int avail_size;
  int save;
  char *p = NULL;
  unsigned char ip_addr[16] = { 0 };

  /* Establish a globaly accessible longjmp environment so we can terminate on severe errors without calling exit(). */
  csql_exit_init ();

  if (setjmp (csql_Exit_env))
    {
      /* perform any dangling cleanup operations */
      csql_exit_cleanup ();
      logddl_destroy ();
      return csql_Exit_status;
    }

  /* initialize message catalog for argument parsing and usage() */
  if (utility_initialize () != NO_ERROR)
    {
      csql_exit (EXIT_FAILURE);
    }

  /* set up prompt and message fields. */
  if (csql_arg->sysadm)
    {
      strncpy_bufsize (csql_Prompt, csql_get_message (CSQL_SYSADM_PROMPT));
    }
  else
    {
      strncpy_bufsize (csql_Prompt, csql_get_message (CSQL_PROMPT));
    }
  avail_size = sizeof (csql_Prompt) - strlen (csql_Prompt) - 1;
  if (avail_size > 0)
    {
      strncat (csql_Prompt, " ", avail_size);
    }
  snprintf (csql_Prompt_offline, sizeof (csql_Prompt_offline), "!%s", csql_Prompt);

  /* as we must use db_open_file_name() to open the input file, it is necessary to be opening csql_Input_fp at this
   * point */
  if (csql_arg->in_file_name != NULL)
    {
#if defined(WINDOWS)
      csql_Input_fp = fopen (csql_arg->in_file_name, "rb");
#else /* !WINDOWS */
      csql_Input_fp = fopen (csql_arg->in_file_name, "r");
#endif /* WINDOWS */
      if (csql_Input_fp == NULL)
    {
      csql_Error_code = CSQL_ERR_OS_ERROR;
      goto error;
    }

#if defined(WINDOWS)
      {
    char tmpchar;       /* open breaks in DLL'S */
    /*
     * Unless an operation is done on this stream before the DLL
     * is entered the file descriptor will be invalid.  This is a bug in
     * MSVC compiler and/or libc.
     */
    tmpchar = fgetc (csql_Input_fp);
    ungetc (tmpchar, csql_Input_fp);
      }
#endif /* WINDOWS */
    }

  if ((csql_arg->in_file_name == NULL) && isatty (fileno (stdin)))
    {
      csql_Is_interactive = true;
    }


  /* initialize error log file */
  if (er_init ("./csql.err", ER_NEVER_EXIT) != NO_ERROR)
    {
      printf ("Failed to initialize error manager.\n");
      csql_Error_code = CSQL_ERR_OS_ERROR;
      goto error;
    }

#if !defined(WINDOWS)
  /* set signal handler */
  register_crash_signal_handler (SIGABRT);
  register_crash_signal_handler (SIGILL);
  register_crash_signal_handler (SIGFPE);
  register_crash_signal_handler (SIGBUS);
  register_crash_signal_handler (SIGSEGV);
  register_crash_signal_handler (SIGSYS);
#endif

  /*
   * login and restart database
   */
  if (csql_arg->sysadm)
    {
      client_type = DB_CLIENT_TYPE_ADMIN_CSQL;

      if (csql_arg->write_on_standby)
    {
      client_type = DB_CLIENT_TYPE_ADMIN_CSQL_WOS;
    }
      else if (csql_arg->skip_vacuum)
    {
      client_type = DB_CLIENT_TYPE_SKIP_VACUUM_ADMIN_CSQL;
    }
    }
  else if (csql_arg->read_only)
    {
      client_type = DB_CLIENT_TYPE_READ_ONLY_CSQL;
    }
  else if (csql_arg->skip_vacuum)
    {
      client_type = DB_CLIENT_TYPE_SKIP_VACUUM_CSQL;
    }
  else
    {
      client_type = DB_CLIENT_TYPE_CSQL;
    }

  if (csql_arg->sysadm_rebuild_catalog)
    {
      csql_apply_catalog_rebuild_mode (csql_arg, &client_type);
    }

  if (db_restart_ex (argv0, csql_arg->db_name, csql_arg->user_name, csql_arg->passwd, NULL, client_type) != NO_ERROR)
    {
      if (!csql_Is_interactive || csql_arg->passwd != NULL || db_error_code () != ER_AU_INVALID_PASSWORD)
    {
      /* not INTERACTIVE mode, or password is given already, or the error code is not password related */
      csql_Error_code = CSQL_ERR_SQL_ERROR;
      goto error;
    }

      /* get password interactively if interactive mode */
      p = getpass ((char *) csql_get_message (CSQL_PASSWD_PROMPT_TEXT));
      if (p[0] == '\0')
    {
      csql_arg->passwd = (char *) NULL; /* to fit into db_login protocol */
    }
      else
    {
      csql_arg->passwd = strdup (p);
    }

      /* try again */
      if (db_restart_ex (argv0, csql_arg->db_name, csql_arg->user_name, csql_arg->passwd, NULL, client_type) !=
      NO_ERROR)
    {
      csql_Error_code = CSQL_ERR_SQL_ERROR;
      goto error;
    }
    }

  er_set_print_property (ER_PRINT_TO_CONSOLE);

  logddl_init (APP_NAME_CSQL);
  logddl_check_ddl_audit_param ();

  if (get_host_ip (ip_addr) == 0)
    {
      logddl_set_ip ((char *) ip_addr);
      db_set_client_ip_addr ((char *) ip_addr);
    }

  if (csql_arg->trigger_action_flag == false)
    {
      db_disable_trigger ();
    }

  if (csql_arg->sysadm && au_is_dba_group_member (Au_user))
    {
      AU_DISABLE (save);
    }

  /* allow environmental setting of the "-s" command line flag to enable automated testing */
  if (prm_get_bool_value (PRM_ID_CSQL_SINGLE_LINE_MODE))
    {
      csql_arg->single_line_execution = true;
    }

  /* record the connection so we know how to clean up on exit */
  csql_Database_connected = true;

#if defined(CSQL_NO_LOGGING)
  if (csql_arg->no_logging && locator_log_force_nologging () != NO_ERROR)
    {
      csql_Error_code = CSQL_ERR_SQL_ERROR;
      goto error;
    }
#endif /* CSQL_NO_LOGGING */

  csql_Editor_cmd[PATH_MAX - 1] = '\0';
  csql_Shell_cmd[PATH_MAX - 1] = '\0';
  csql_Print_cmd[PATH_MAX - 1] = '\0';
  csql_Pager_cmd[PATH_MAX - 1] = '\0';
  csql_Formatter_cmd[PATH_MAX - 1] = '\0';

  env = getenv ("CUBRID_CSQL_EDITOR");
  env = env != NULL ? env : getenv ("EDITOR");
  if (env)
    {
      strncpy (csql_Editor_cmd, env, PATH_MAX - 1);
    }

  env = getenv ("CUBRID_CSQL_SHELL");
  env = env != NULL ? env : getenv ("SHELL");
  if (env)
    {
      strncpy (csql_Shell_cmd, env, PATH_MAX - 1);
    }

  env = getenv ("CUBRID_CSQL_FORMATTER");
  env = env != NULL ? env : getenv ("FORMATTER");
  if (env)
    {
      strncpy (csql_Formatter_cmd, env, PATH_MAX - 1);
    }

  env = getenv ("CUBRID_CSQL_PROMPT");
  if (!csql_arg->sysadm && env && *env != '\0')
    {
      csql_Prompt_user_defined = CSQL_PROMPT_USER_DEFINED;
      strcpy (csql_Prompt_format, env);
    }

  if (csql_arg->nopager)
    {
      csql_Pager_cmd[0] = '\0';
    }

  lang_init_console_txt_conv ();

  if (csql_Is_interactive)
    {
      /* handling Ctrl-C */
#if defined(WINDOWS)
      SetConsoleCtrlHandler ((PHANDLER_ROUTINE) signal_intr, TRUE);
#else
      if (os_set_signal_handler (SIGINT, signal_intr) == SIG_ERR)
    {
      csql_Error_code = CSQL_ERR_OS_ERROR;
      goto error;
    }
#endif

#if !defined(WINDOWS)
      if (os_set_signal_handler (SIGQUIT, signal_intr) == SIG_ERR)
    {
      csql_Error_code = CSQL_ERR_OS_ERROR;
      goto error;
    }
#endif /* !WINDOWS */
    }

  er_set_print_property (ER_DO_NOT_PRINT);

  start_csql (csql_arg);

  csql_exit (EXIT_SUCCESS); /* not reachable code, actually */

error:
  nonscr_display_error (csql_Scratch_text, SCRATCH_TEXT_LEN);
  csql_exit (EXIT_FAILURE);
  er_final (ER_ALL_FINAL);
  return EXIT_FAILURE;      /* won't get here really */
}

/*
 * csql_get_message() - get a string of the csql-utility from the catalog
 *   return: message string
 *   message_index(in): an index of the message string
 */
const char *
csql_get_message (int message_index)
{
  return (msgcat_message (MSGCAT_CATALOG_CSQL, MSGCAT_CSQL_SET_CSQL, message_index));
}

/*
 * csql_set_column_width_info() - insert column_name and column_width
 *                                in csql_column_width_info_list
 *   return: int
 *   column_name(in): column_name
 *   column_width(in): column_width
 */
int
csql_set_column_width_info (const char *column_name, int column_width)
{
  CSQL_COLUMN_WIDTH_INFO *temp_list;
  char *temp_name;
  int i, index;

  if (column_name == NULL || column_width < 0)
    {
      csql_Error_code = CSQL_ERR_INVALID_ARG_COMBINATION;

      return CSQL_FAILURE;
    }

  if (csql_column_width_info_list == NULL)
    {
      if (initialize_csql_column_width_info_list () != CSQL_SUCCESS)
    {
      return CSQL_FAILURE;
    }
    }

  if (csql_column_width_info_list_index >= csql_column_width_info_list_size)
    {
      temp_list =
    (CSQL_COLUMN_WIDTH_INFO *) realloc (csql_column_width_info_list,
                        sizeof (CSQL_COLUMN_WIDTH_INFO) * (csql_column_width_info_list_size * 2));
      if (temp_list == NULL)
    {
      csql_Error_code = CSQL_ERR_NO_MORE_MEMORY;

      return CSQL_FAILURE;
    }

      csql_column_width_info_list_size *= 2;
      csql_column_width_info_list = temp_list;
      for (i = csql_column_width_info_list_index; i < csql_column_width_info_list_size; i++)
    {
      csql_column_width_info_list[i].name = NULL;
      csql_column_width_info_list[i].width = 0;
    }
    }

  index = NOT_FOUND;
  for (i = 0; i < csql_column_width_info_list_index; i++)
    {
      if (strcasecmp (column_name, csql_column_width_info_list[i].name) == 0)
    {
      index = i;
      break;
    }
    }

  if (index == NOT_FOUND)
    {
      index = csql_column_width_info_list_index;
      csql_column_width_info_list_index++;
    }

  if (csql_column_width_info_list[index].name == NULL)
    {
      temp_name = strdup (column_name);
      if (temp_name == NULL)
    {
      csql_Error_code = CSQL_ERR_NO_MORE_MEMORY;

      return CSQL_FAILURE;
    }

      csql_column_width_info_list[index].name = temp_name;
      csql_column_width_info_list[index].width = column_width;
    }
  else
    {
      csql_column_width_info_list[index].width = column_width;
    }

  return CSQL_SUCCESS;
}

/*
 * csql_get_column_width() - get column_width related column_name
 *   return: column_width
 *   column_name(in): column_name
 */
int
csql_get_column_width (const char *column_name)
{
  char name_without_space[1024];
  char *result;
  int i;

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

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

  strncpy (name_without_space, column_name, sizeof (name_without_space) - 1);
  name_without_space[sizeof (name_without_space) - 1] = '\0';
  result = trim (name_without_space);
  if (result == NULL)
    {
      return 0;
    }

  for (i = 0; i < csql_column_width_info_list_index; i++)
    {
      if (strcasecmp (result, csql_column_width_info_list[i].name) == 0)
    {
      return csql_column_width_info_list[i].width;
    }
    }

  return 0;
}

/*
 * get_column_name_argument() - get column_name and value pointer from argument
 *   return: int
 *   column_name(out): column name
 *   val_str(out): value string in argument
 *   argument(in): argument
 */
static int
get_column_name_argument (char **column_name, char **val_str, char *argument)
{
  char *p;

  assert (column_name != NULL && val_str != NULL && argument != NULL);

  *column_name = NULL;
  *val_str = NULL;

  /* argument : "column_name=value" */
  *column_name = argument;

  p = strrchr (*column_name, '=');
  if (p != NULL)
    {
      *p = '\0';
      *val_str = (p + 1);
    }

  trim (*column_name);

  /* max column_name size is 254 */
  if (strlen (*column_name) > 254)
    {
      return CSQL_FAILURE;
    }

  return CSQL_SUCCESS;
}

/*
 * csql_set_trace() - set auto trace on or off
 *   return:
 *   arg_str(in):
 */
static void
csql_set_trace (const char *arg_str)
{
  char line[128];
  char format[128], *p;

  if (arg_str != NULL)
    {
      if (strncmp (arg_str, "on", 2) == 0)
    {
      prm_set_bool_value (PRM_ID_QUERY_TRACE, true);
      csql_Query_trace = true;

      if (sscanf (arg_str, "on %127s", format) == 1)
        {
          p = trim (format);

          if (strncmp (p, "text", 4) == 0)
        {
          prm_set_integer_value (PRM_ID_QUERY_TRACE_FORMAT, QUERY_TRACE_TEXT);
        }
          else if (strncmp (p, "json", 4) == 0)
        {
          prm_set_integer_value (PRM_ID_QUERY_TRACE_FORMAT, QUERY_TRACE_JSON);
        }
        }
    }
      else if (!strncmp (arg_str, "off", 3))
    {
      prm_set_bool_value (PRM_ID_QUERY_TRACE, false);
      csql_Query_trace = false;
    }
    }

  if (prm_get_bool_value (PRM_ID_QUERY_TRACE) == true)
    {
      if (prm_get_integer_value (PRM_ID_QUERY_TRACE_FORMAT) == QUERY_TRACE_JSON)
    {
      snprintf (line, 128, "trace on json");
    }
      else
    {
      snprintf (line, 128, "trace on text");
    }
    }
  else
    {
      snprintf (line, 128, "trace off");
    }

  csql_append_more_line (0, line);
  csql_display_more_lines ("Query Trace");
  csql_free_more_lines ();
}

/*
 * csql_execute_query() -
 *   return:
 */
static int
csql_execute_query (const char *stmts)
{
  DB_SESSION *session = NULL;
  DB_QUERY_RESULT *result = NULL;
  int stmt_id = -1;
  int db_error = ER_FAILED;

  session = db_open_buffer (stmts);
  if (session == NULL)
    {
      goto end;
    }

  stmt_id = db_compile_statement (session);
  if (stmt_id < 0)
    {
      db_error = stmt_id;
      goto end;
    }

  db_error = db_execute_statement (session, stmt_id, &result);
  if (db_error < 0)
    {
      goto end;
    }

  db_error = NO_ERROR;

end:

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

  if (session != NULL)
    {
      db_close_session (session);
    }

  return db_error;
}

/*
 * csql_display_trace() -
 *   return:
 */
static void
csql_display_trace (void)
{
  const char *stmts = NULL;
  DB_SESSION *session = NULL;
  int stmt_id, db_error;
  DB_QUERY_RESULT *result = NULL;
  DB_VALUE trace;
  FILE *pf;
  int save_row_count;

  er_clear ();
  db_set_interrupt (0);
  db_make_null (&trace);
  save_row_count = db_get_row_count_cache ();

  stmts = "SHOW TRACE";

  session = db_open_buffer (stmts);
  if (session == NULL)
    {
      return;
    }

  stmt_id = db_compile_statement (session);

  if (stmt_id < 0)
    {
      goto end;
    }

  db_error = db_execute_statement (session, stmt_id, &result);

  if (db_error < 0)
    {
      goto end;
    }

  (void) db_query_set_copy_tplvalue (result, 0 /* peek */ );

  if (db_query_first_tuple (result) < 0)
    {
      goto end;
    }

  if (db_query_get_tuple_value (result, 0, &trace) < 0)
    {
      goto end;
    }

  if (DB_VALUE_TYPE (&trace) == DB_TYPE_STRING)
    {
      pf = csql_popen (csql_Pager_cmd, csql_Output_fp);
      fprintf (pf, "\n=== Auto Trace ===\n");
      fprintf (pf, "%s\n", db_get_char (&trace));
      csql_pclose (pf, csql_Output_fp);
    }

end:

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

  if (session != NULL)
    {
      db_close_session (session);
    }

  db_update_row_count_cache (save_row_count);

  return;
}

static bool
csql_is_auto_commit_requested (const CSQL_ARGUMENT * csql_arg)
{
  assert (csql_arg != NULL);
  return csql_arg->auto_commit && prm_get_bool_value (PRM_ID_CSQL_AUTO_COMMIT);
}

static int
get_host_ip (unsigned char *ip_addr)
{
  char hostname[CUB_MAXHOSTNAMELEN];
  struct hostent *hp;
  char *ip;

  if (gethostname (hostname, sizeof (hostname)) < 0)
    {
      return -1;
    }
  if ((hp = gethostbyname_uhost (hostname)) == NULL)
    {
      return -1;
    }

  ip = inet_ntoa (*(struct in_addr *) *hp->h_addr_list);
  memcpy (ip_addr, ip, strlen (ip));

  return 0;
}

static int
csql_connect (char *argument, CSQL_ARGUMENT * csql_arg)
{
  /*dbname can be stored DB name+ @ + remote hostname + \0 */
  char buf[DB_MAX_USER_LENGTH + DB_NAME_LEN + CUB_MAXHOSTNAMELEN + 1] = { 0, };
  const char *delim = " \n";
  char *save_ptr_strtok = NULL;
  const char *db_name_ptr = NULL;
  char *user_name_ptr = NULL;
  char *host_name_ptr = NULL;
  char *p = NULL;
  const char *err_msg;
  CSQL_ARGUMENT csql_new_arg;

  csql_Error_code = NO_ERROR;

  if (argument == NULL)
    {
      err_msg = (*csql_get_message) (CSQL_MSG_TOO_FEW_ARGS);
      fprintf (csql_Output_fp, "%s\n", err_msg);
      return ER_FAILED;
    }

  memset (&csql_new_arg, 0, sizeof csql_new_arg);

  strncpy (buf, argument, sizeof (buf) - 1);

  if ((user_name_ptr = strtok_r (buf, delim, &save_ptr_strtok)) == NULL)
    {
      csql_Error_code = CSQL_ERR_SQL_ERROR;
      return ER_FAILED;
    }

  /*find db name following the user name */
  db_name_ptr = strtok_r (NULL, delim, &save_ptr_strtok);
  if (db_name_ptr == NULL)
    {
      db_name_ptr = csql_arg->db_name;
    }
  else
    {
      if (csql_arg->sa_mode == true)
    {
      fprintf (csql_Output_fp, "Cannot connect to other DB in the --SA-mode.\n");
      return ER_FAILED;
    }
    }

  memcpy (&csql_new_arg, csql_arg, sizeof (CSQL_ARGUMENT));

#if defined(CS_MODE)
  memset (boot_Host_connected, 0, sizeof (boot_Host_connected));
#endif /* CS_MODE */

  csql_exit_session (0, false);

  /*Failed to access other host or db and then access formal db_name */
  if (csql_Database_connected)
    {
      csql_Database_connected = false;
      db_shutdown ();
    }

  er_init ("./csql.err", ER_NEVER_EXIT);
  csql_new_arg.passwd = (char *) NULL;

  if (db_restart_ex (UTIL_CSQL_NAME, db_name_ptr, user_name_ptr, NULL, NULL, db_get_client_type ()) != NO_ERROR)
    {
      if (csql_Is_interactive && db_error_code () == ER_AU_INVALID_PASSWORD)
    {
      p = getpass ((char *) csql_get_message (CSQL_PASSWD_PROMPT_TEXT));

      /* try again */
      if (db_restart_ex (UTIL_CSQL_NAME, db_name_ptr, user_name_ptr, p, NULL, db_get_client_type ()) != NO_ERROR)
        {
          csql_Error_code = CSQL_ERR_SQL_ERROR;
          csql_check_server_down ();
          fprintf (csql_Output_fp, "Warning: current CSQL session is disconnected.\n");

          return ER_FAILED;
        }

      if (p[0] == '\0')
        {
          csql_new_arg.passwd = (char *) NULL;  /* to fit into db_login protocol */
        }
      else
        {
          csql_new_arg.passwd = strdup (p);
        }
    }
      else
    {
      csql_Error_code = CSQL_ERR_SQL_ERROR;
      csql_check_server_down ();
      fprintf (csql_Output_fp, "Warning: current CSQL session is disconnected.\n");

      return ER_FAILED;

    }
    }

/*If login is success, copy csql_new_arg to csql_arg*/
  csql_new_arg.user_name = strdup (user_name_ptr);
  strcpy (csql_Db_name, db_name_ptr);
  csql_new_arg.db_name = csql_Db_name;

  FREE_MEM ((char *) csql_arg->user_name);
  FREE_MEM ((char *) csql_arg->passwd);

  memcpy (csql_arg, &csql_new_arg, sizeof (CSQL_ARGUMENT));

  if (csql_arg->sysadm && au_is_dba_group_member (Au_user))
    {
      int dummy;
      AU_DISABLE (dummy);
    }
  csql_Database_connected = true;

  if (csql_arg->trigger_action_flag == false)
    {
      db_disable_trigger ();
    }

  fprintf (csql_Output_fp, "Connected.\n");


/*If connect is success, copy csql_new_arg to csql_arg*/

  return NO_ERROR;
}