Skip to content

File csql_result.c

File List > cubrid > src > executables > csql_result.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_result.c : Query execution / result handling routine
 */

#ident "$Id$"

#include "config.h"

#include <assert.h>
#include <float.h>
#include <setjmp.h>
#include <signal.h>

#include "csql.h"
#include "dbtran_def.h"
#include "dbtype.h"
#include "memory_alloc.h"
#include "object_primitive.h"
#include "porting.h"
#include "transaction_cl.h"

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

/* max columns to display each data type
 * NOTE: some of these are totally dependent on report-writer's
 * rendering library.
 */
#define MAX_SHORT_DISPLAY_LENGTH      6
#define MAX_INTEGER_DISPLAY_LENGTH    11
#define MAX_BIGINT_DISPLAY_LENGTH     20
#define MAX_FLOAT_DISPLAY_LENGTH      (FLT_DIG + 7)
#define MAX_DOUBLE_DISPLAY_LENGTH     (DBL_DIG + 9)
#define MAX_TIME_DISPLAY_LENGTH       11
#define MAX_TIMEZONE_DISPLAY_LENGTH   18
#define MAX_UTIME_DISPLAY_LENGTH      25
#define MAX_TIMESTAMPTZ_DISPLAY_LENGTH  \
  (MAX_UTIME_DISPLAY_LENGTH + MAX_TIMEZONE_DISPLAY_LENGTH)
#define MAX_DATETIME_DISPLAY_LENGTH       29
#define MAX_DATETIMETZ_DISPLAY_LENGTH  \
  (MAX_DATETIME_DISPLAY_LENGTH + MAX_TIMEZONE_DISPLAY_LENGTH)
#define MAX_DATE_DISPLAY_LENGTH       10
#define MAX_MONETARY_DISPLAY_LENGTH   20
#define MAX_DEFAULT_DISPLAY_LENGTH    20
#define STRING_TYPE_PREFIX_SUFFIX_LENGTH  2
#define BIT_TYPE_PREFIX_SUFFIX_LENGTH     3

/* structure for current query result information */
typedef struct
{
  DB_QUERY_RESULT *query_result;
  int num_attrs;
  char **attr_names;
  int *attr_lengths;
  DB_TYPE *attr_types;
  int max_attr_name_length;
  CUBRID_STMT_TYPE curr_stmt_type;
  int curr_stmt_line_no;
} CUR_RESULT_INFO;

typedef struct
{
  CUBRID_STMT_TYPE stmt_type;
  const char *cmd_string;
} CSQL_CMD_STRING_TABLE;

static CSQL_CMD_STRING_TABLE csql_Cmd_string_table[] = {
  {CUBRID_STMT_SELECT, "SELECT"},
  {CUBRID_STMT_SELECT_UPDATE, "SELECT"},
  {CUBRID_STMT_CALL, "CALL"},
  {CUBRID_STMT_EVALUATE, "EVALUATE"},
  {CUBRID_STMT_GET_ISO_LVL, "GET ISOLATION LEVEL"},
  {CUBRID_STMT_GET_TIMEOUT, "GET LOCK TIMEOUT"},
  {CUBRID_STMT_GET_OPT_LVL, "GET OPTIMIZATION"},
  {CUBRID_STMT_GET_TRIGGER, "GET TRIGGER"},
  {CUBRID_STMT_UPDATE, "UPDATE"},
  {CUBRID_STMT_DELETE, "DELETE"},
  {CUBRID_STMT_INSERT, "INSERT"},
  {CUBRID_STMT_ALTER_CLASS, "ALTER"},
  {CUBRID_STMT_COMMIT_WORK, "COMMIT"},
  {CUBRID_STMT_CREATE_CLASS, "CREATE"},
  {CUBRID_STMT_CREATE_INDEX, "CREATE INDEX"},
  {CUBRID_STMT_DROP_DATABASE, "DROP LDB"},
  {CUBRID_STMT_DROP_CLASS, "DROP"},
  {CUBRID_STMT_DROP_INDEX, "DROP INDEX"},
  {CUBRID_STMT_ALTER_INDEX, "ALTER INDEX"},
  {CUBRID_STMT_DROP_LABEL, "DROP "},
  {CUBRID_STMT_RENAME_CLASS, "RENAME"},
  {CUBRID_STMT_ROLLBACK_WORK, "ROLLBACK"},
  {CUBRID_STMT_GRANT, "GRANT"},
  {CUBRID_STMT_REVOKE, "REVOKE"},
  {CUBRID_STMT_CREATE_USER, "CREATE USER"},
  {CUBRID_STMT_DROP_USER, "DROP USER"},
  {CUBRID_STMT_ALTER_USER, "ALTER USER"},
  {CUBRID_STMT_UPDATE_STATS, "UPDATE STATISTICS"},
  {CUBRID_STMT_GET_STATS, "GET STATISTICS"},
  {CUBRID_STMT_SCOPE, "SCOPE"},
  {CUBRID_STMT_REGISTER_DATABASE, "REGISTER"},
  {CUBRID_STMT_CREATE_TRIGGER, "CREATE TRIGGER"},
  {CUBRID_STMT_DROP_TRIGGER, "DROP TRIGGER"},
  {CUBRID_STMT_SET_OPT_LVL, "SET OPTIMIZATION"},
  {CUBRID_STMT_SET_SYS_PARAMS, "SET SYSTEM PARAMETERS"},
  {CUBRID_STMT_SET_TRIGGER, "SET TRIGGER"},
  {CUBRID_STMT_SAVEPOINT, "SAVEPOINT"},
  {CUBRID_STMT_PREPARE, "PREPARE"},
  {CUBRID_STMT_ATTACH, "ATTACH"},
  {CUBRID_STMT_USE, "USE/EXCLUDE"},
  {CUBRID_STMT_REMOVE_TRIGGER, "REMOVE TRIGGER"},
  {CUBRID_STMT_RENAME_TRIGGER, "RENAME TRIGGER"},
  {CUBRID_STMT_ON_LDB, "ON LDB"},
  {CUBRID_STMT_GET_LDB, "GET LDB"},
  {CUBRID_STMT_SET_LDB, "SET LDB"},
  {CUBRID_STMT_ALTER_SERIAL, "ALTER SERIAL"},
  {CUBRID_STMT_CREATE_SERIAL, "CREATE SERIAL"},
  {CUBRID_STMT_DROP_SERIAL, "DROP SERIAL"},
  {CUBRID_STMT_CREATE_STORED_PROCEDURE, "CREATE PROCEDURE"},
  {CUBRID_STMT_DROP_STORED_PROCEDURE, "DROP PROCEDURE"},
  {CUBRID_STMT_TRUNCATE, "TRUNCATE"},
  {CUBRID_STMT_SET_SESSION_VARIABLES, "SET"},
  {CUBRID_STMT_DROP_SESSION_VARIABLES, "DROP VARIABLE"},
  {CUBRID_STMT_DO, "DO"},
  {CUBRID_STMT_SET_NAMES, "SET NAMES"},
  {CUBRID_STMT_VACUUM, "VACUUM"},
  {CUBRID_STMT_SET_TIMEZONE, "SET TIMEZONE"},
  {CUBRID_STMT_CREATE_SERVER, "CREATE SERVER"},
  {CUBRID_STMT_DROP_SERVER, "DROP SERVER"},
  {CUBRID_STMT_RENAME_SERVER, "RENAME SERVER"},
  {CUBRID_STMT_ALTER_SERVER, "ALTER SERVER"},
  {CUBRID_STMT_ALTER_SYNONYM, "ALTER SYNONYM"},
  {CUBRID_STMT_CREATE_SYNONYM, "CREATE SYNONYM"},
  {CUBRID_STMT_DROP_SYNONYM, "DROP SYNONYM"},
  {CUBRID_STMT_RENAME_SYNONYM, "RENAME SYNONYM"}
};

static const char *csql_Isolation_level_string[] = {
  "UNKNOWN",
  "UNKNOWN",
  "UNKNOWN",
  "UNKNOWN",
  "READ COMMITTED",
  "REPEATABLE READ",
  "SERIALIZABLE"
};

static jmp_buf csql_Jmp_buf;

static const char *csql_cmd_string (CUBRID_STMT_TYPE stmt_type, const char *default_string);
static void display_empty_result (int stmt_type, int line_no);
static char **get_current_result (int **len, const CUR_RESULT_INFO * result_info, const CSQL_ARGUMENT * csql_arg);
static int write_results_to_stream (const CSQL_ARGUMENT * csql_arg, FILE * fp, const CUR_RESULT_INFO * result_info);
static char *uncontrol_strdup (const char *from);
static char *uncontrol_strndup (const char *from, int length);
static int calculate_width (int column_width, int string_width, int origin_width, DB_TYPE attr_type, bool is_null);
static bool is_string_type (DB_TYPE type);
static bool is_bit_type (DB_TYPE type);
static bool is_cuttable_type_by_string_width (DB_TYPE type);
static bool is_type_that_has_suffix (DB_TYPE type);

/*
 * csql_results() - display the result
 *   return: none
 *   csql_arg(in): csql argument
 *   result(in): query result structure.
 *   attr_spec(in): result attribute spec structure
 *   line_no(in): line number on which the statement appears
 *   stmt_type(in): query statement type
 *
 * Note: If `result' is NULL, no results is assumed.
 */
void
csql_results (const CSQL_ARGUMENT * csql_arg, DB_QUERY_RESULT * result, DB_QUERY_TYPE * attr_spec, int line_no,
          CUBRID_STMT_TYPE stmt_type)
{
  int i;
  DB_QUERY_TYPE *t;     /* temp ptr for attr_spec */
  int err;
  int *attr_name_lengths = NULL;    /* attribute name length array */
  CUR_RESULT_INFO result_info;
  int num_attrs = 0;
  char **attr_names = NULL;
  int *attr_lengths = NULL;
  DB_TYPE *attr_types = NULL;
  int max_attr_name_length = 0;

  /* trivial case - no results */
  if (result == NULL || (err = db_query_first_tuple (result)) == DB_CURSOR_END)
    {
      if (csql_arg->plain_output == false && csql_arg->query_output == false && csql_arg->loaddb_output == false)
    {
      display_empty_result (stmt_type, line_no);
    }
      return;
    }

  if (err < 0)
    {
      csql_Error_code = CSQL_ERR_SQL_ERROR;
      goto error;
    }

  for (t = attr_spec; t != NULL; t = db_query_format_next (t), num_attrs++)
    {
      ;
    }

  /* allocate pointer array for attr names and int array for attr lengths */
  attr_names = (char **) malloc (sizeof (char *) * num_attrs);
  if (attr_names == NULL)
    {
      csql_Error_code = CSQL_ERR_NO_MORE_MEMORY;
      goto error;
    }
  for (i = 0; i < num_attrs; i++)
    {
      attr_names[i] = (char *) NULL;
    }
  attr_name_lengths = (int *) malloc (sizeof (int) * num_attrs);
  attr_lengths = (int *) malloc (sizeof (int) * num_attrs);
  attr_types = (DB_TYPE *) malloc (sizeof (DB_TYPE) * num_attrs);
  if (attr_name_lengths == NULL || attr_lengths == NULL || attr_types == NULL)
    {
      csql_Error_code = CSQL_ERR_NO_MORE_MEMORY;
      goto error;
    }

  /* get the result attribute names */

  max_attr_name_length = 0;

  for (i = 0, t = attr_spec; t != NULL; t = db_query_format_next (t), i++)
    {
      const char *temp;

      temp = db_query_format_name (t);
      if (temp == NULL)
    {
      attr_names[i] = (char *) malloc (7);
      if (attr_names[i] == NULL)
        {
          csql_Error_code = CSQL_ERR_NO_MORE_MEMORY;
          goto error;
        }
      strcpy (attr_names[0], "Result");
    }
      else
    {
      bool is_console_conv = false;

      /* console encoded attribute name */
      if (csql_text_utf8_to_console != NULL)
        {
          char *attr_name_console_encoded = NULL;
          int attr_name_console_length = -1;

          /* try to convert attribute name from utf-8 to console */
          if ((*csql_text_utf8_to_console)
          (temp, strlen (temp), &attr_name_console_encoded, &attr_name_console_length) == NO_ERROR)
        {
          if (attr_name_console_encoded != NULL)
            {
              free_and_init (attr_names[i]);
              attr_names[i] = attr_name_console_encoded;
              is_console_conv = true;
            }
        }
        }

      if (!is_console_conv)
        {
          attr_names[i] = uncontrol_strdup (temp);
          if (attr_names[i] == NULL)
        {
          goto error;
        }
        }
    }
      attr_name_lengths[i] = strlen (attr_names[i]);
      max_attr_name_length = MAX (max_attr_name_length, attr_name_lengths[i]);
      attr_types[i] = db_query_format_type (t);

      switch (attr_types[i])
    {
    case DB_TYPE_SHORT:
      attr_lengths[i] = MAX (MAX_SHORT_DISPLAY_LENGTH, attr_name_lengths[i]);
      break;
    case DB_TYPE_INTEGER:
      attr_lengths[i] = MAX (MAX_INTEGER_DISPLAY_LENGTH, attr_name_lengths[i]);
      break;
    case DB_TYPE_BIGINT:
      attr_lengths[i] = MAX (MAX_BIGINT_DISPLAY_LENGTH, attr_name_lengths[i]);
      break;
    case DB_TYPE_FLOAT:
      attr_lengths[i] = MAX (MAX_FLOAT_DISPLAY_LENGTH, attr_name_lengths[i]);
      break;
    case DB_TYPE_DOUBLE:
      attr_lengths[i] = MAX (MAX_DOUBLE_DISPLAY_LENGTH, attr_name_lengths[i]);
      break;
    case DB_TYPE_TIME:
      attr_lengths[i] = -MAX (MAX_TIME_DISPLAY_LENGTH, attr_name_lengths[i]);
      break;
    case DB_TYPE_TIMESTAMP:
      attr_lengths[i] = -MAX (MAX_UTIME_DISPLAY_LENGTH, attr_name_lengths[i]);
      break;
    case DB_TYPE_TIMESTAMPTZ:
    case DB_TYPE_TIMESTAMPLTZ:
      attr_lengths[i] = -MAX (MAX_TIMESTAMPTZ_DISPLAY_LENGTH, attr_name_lengths[i]);
      break;
    case DB_TYPE_DATETIME:
      attr_lengths[i] = -MAX (MAX_DATETIME_DISPLAY_LENGTH, attr_name_lengths[i]);
      break;
    case DB_TYPE_DATETIMETZ:
    case DB_TYPE_DATETIMELTZ:
      attr_lengths[i] = -MAX (MAX_DATETIMETZ_DISPLAY_LENGTH, attr_name_lengths[i]);
      break;
    case DB_TYPE_DATE:
      attr_lengths[i] = -MAX (MAX_DATE_DISPLAY_LENGTH, attr_name_lengths[i]);
      break;
    case DB_TYPE_MONETARY:
      attr_lengths[i] = MAX (MAX_MONETARY_DISPLAY_LENGTH, attr_name_lengths[i]);
      break;
    default:
      attr_lengths[i] = -MAX_DEFAULT_DISPLAY_LENGTH;
      break;
    }
    }

  result_info.query_result = result;
  result_info.num_attrs = num_attrs;
  result_info.attr_names = attr_names;
  result_info.attr_lengths = attr_lengths;
  result_info.attr_types = attr_types;
  result_info.max_attr_name_length = max_attr_name_length;
  result_info.curr_stmt_type = stmt_type;
  result_info.curr_stmt_line_no = line_no;

  /*
   * Write_results_to_stream may need to fetch instances if value type is object or set of objects.
   * Make sure fetch type is not set to current version since all the versions are identified by
   * the same OID and snapshot must be considered to reach the visible version again. */
  assert (TM_TRAN_READ_FETCH_VERSION () != LC_FETCH_CURRENT_VERSION);
  if (write_results_to_stream (csql_arg, csql_Output_fp, &result_info) == CSQL_FAILURE)
    {
      if (csql_Error_code == CSQL_ERR_SQL_ERROR)
    {
      goto error;
    }
      else
    {
      nonscr_display_error (csql_Scratch_text, SCRATCH_TEXT_LEN);
    }
    }

  /* free memories */
  if (attr_names != NULL)
    {
      for (i = 0; i < num_attrs; i++)
    {
      if (attr_names[i] != NULL)
        {
          free_and_init (attr_names[i]);
        }
    }
      free_and_init (attr_names);
    }
  if (attr_name_lengths != NULL)
    {
      free_and_init (attr_name_lengths);
    }
  if (attr_lengths != NULL)
    {
      free_and_init (attr_lengths);
    }
  if (attr_types != NULL)
    {
      free_and_init (attr_types);
    }

  return;

error:

  if (csql_Error_code == CSQL_ERR_SQL_ERROR)
    {
      csql_display_csql_err (line_no, 0);
      csql_check_server_down ();
      /* for correct csql return code */
      csql_Num_failures++;
    }

  /* free memories */
  if (attr_names != NULL)
    {
      for (i = 0; i < num_attrs; i++)
    {
      if (attr_names[i] != NULL)
        {
          free_and_init (attr_names[i]);
        }
    }
      free_and_init (attr_names);
    }
  if (attr_name_lengths != NULL)
    {
      free_and_init (attr_name_lengths);
    }
  if (attr_lengths != NULL)
    {
      free_and_init (attr_lengths);
    }
  if (attr_types != NULL)
    {
      free_and_init (attr_types);
    }
}

/*
 * csql_cmd_string() - return the command string associated with a statement enum
 *   return:  const char*
 *   stmt_type(in): statement enum
 *   default_string(in): default command string if stmt_type is invallid
 */
static const char *
csql_cmd_string (CUBRID_STMT_TYPE stmt_type, const char *default_string)
{
  int i;
  int table_size = DIM (csql_Cmd_string_table);

  for (i = 0; i < table_size; i++)
    {
      if (csql_Cmd_string_table[i].stmt_type == stmt_type)
    {
      return (csql_Cmd_string_table[i].cmd_string);
    }
    }
  return default_string;
}

/*
 * display_empty_result() - display the empty result message
 *   return: none
 *   stmt_type(in): current statement type
 *   line_no(in): current statement line number
 */
static void
display_empty_result (int stmt_type, int line_no)
{
  FILE *pf;         /* pipe stream to pager */

  snprintf (csql_Scratch_text, SCRATCH_TEXT_LEN,
        msgcat_message (MSGCAT_CATALOG_CSQL, MSGCAT_CSQL_SET_CSQL, CSQL_RESULT_STMT_TITLE_FORMAT),
        csql_cmd_string ((CUBRID_STMT_TYPE) stmt_type, ""), line_no);

  pf = csql_popen (csql_Pager_cmd, csql_Output_fp);

  csql_fputs ("\n=== ", pf);
  csql_fputs_console_conv (csql_Scratch_text, pf);
  csql_fputs (" ===\n\n", pf);
  csql_fputs_console_conv (msgcat_message
               (MSGCAT_CATALOG_CSQL, MSGCAT_CSQL_SET_CSQL, CSQL_STAT_NONSCR_EMPTY_RESULT_TEXT), pf);
  csql_fputs ("\n", pf);

  csql_pclose (pf, csql_Output_fp);

  return;
}

/*
 * get_current_result() - get the attribute values of the current result
 *   return: pointer newly allocated value array. On error, NULL.
 *   lengths(out): lengths of returned values
 *   result_info(in): pointer to current query result info structure
 *   plain_output(in): refine string for plain output
 *   query_output(in): refine string for query output
 *   loaddb_output(in): refine string for loaddb output
 *   column_enclosure(in): column enclosure for query output
 *
 * Note:
 *   Caller should be responsible for free the return array and its elements.
 */
static char **
get_current_result (int **lengths, const CUR_RESULT_INFO * result_info, const CSQL_ARGUMENT * csql_arg)
{
  int i;
  char **val = NULL;        /* temporary array for values */
  int *len = NULL;      /* temporary array for lengths */
  DB_VALUE db_value;
  CUBRID_STMT_TYPE stmt_type = result_info->curr_stmt_type;
  DB_QUERY_RESULT *result = result_info->query_result;
  int num_attrs = result_info->num_attrs;

  db_make_null (&db_value);

  val = (char **) malloc (sizeof (char *) * num_attrs);
  if (val == NULL)
    {
      csql_Error_code = CSQL_ERR_NO_MORE_MEMORY;
      goto error;
    }
  memset (val, 0, sizeof (char *) * num_attrs);

  len = (int *) malloc (sizeof (int) * num_attrs);
  if (len == NULL)
    {
      csql_Error_code = CSQL_ERR_NO_MORE_MEMORY;
      goto error;
    }
  memset (len, 0, sizeof (int) * num_attrs);

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

  /* get attribute values */
  for (i = 0; i < num_attrs; i++)
    {
      DB_TYPE value_type;

      if (db_query_get_tuple_value (result, i, &db_value) < 0)
    {
      csql_Error_code = CSQL_ERR_SQL_ERROR;
      goto error;
    }

      value_type = DB_VALUE_TYPE (&db_value);

      /*
       * This assert is intended to validate that the server returned the
       * expected types for the query results. See the note in
       * pt_print_value () regarding XASL caching.
       */
      /*
       * TODO fix this assert if it fails in valid cases. Perhaps it should
       *      allow DB_TYPE_POINTER? What about DB_TYPE_ERROR?
       */
      /*
       * TODO add a similar check to the ux_* and/or cci_* and/or the server
       *      functions so that the results' types returned through sockets in
       *      CS_MODE are validated.
       */
      assert (value_type == DB_TYPE_NULL
          /* UNKNOWN, maybe host variable */
          || result_info->attr_types[i] == DB_TYPE_NULL || result_info->attr_types[i] == DB_TYPE_VARIABLE
          || value_type == result_info->attr_types[i]
          || (TP_IS_CHAR_TYPE (value_type) && TP_IS_CHAR_TYPE (result_info->attr_types[i])));

      switch (value_type)
    {
    case DB_TYPE_NULL:  /* null value */
      val[i] = (char *) malloc (5);
      if (val[i] == NULL)
        {
          csql_Error_code = CSQL_ERR_NO_MORE_MEMORY;
          goto error;
        }
      strcpy (val[i], "NULL");
      break;

    case DB_TYPE_POINTER:   /* pointer type */
      val[i] = (char *) malloc (40);
      if (val[i] == NULL)
        {
          csql_Error_code = CSQL_ERR_NO_MORE_MEMORY;
          goto error;
        }
      sprintf (val[i], "pointer value (%p)", (void *) db_get_pointer (&db_value));
      break;

    case DB_TYPE_ERROR: /* error type */
      val[i] = (char *) malloc (40);
      if (val[i] == NULL)
        {
          csql_Error_code = CSQL_ERR_NO_MORE_MEMORY;
          goto error;
        }
      sprintf (val[i], "error code (%d)", db_get_error (&db_value));
      break;

    default:        /* other types */
      /*
       * If we are printing the isolation level, we need to
       * interpret it for the user, not just return a meaningless number.
       *
       * Also interpret a lock timeout value of -1
       */
      if (stmt_type == CUBRID_STMT_GET_ISO_LVL)
        {
          int async_ws, iso_lvl;

          async_ws = db_get_int (&db_value) & TRAN_ASYNC_WS_BIT;
          iso_lvl = db_get_int (&db_value) & TRAN_ISO_LVL_BITS;

          val[i] = (char *) malloc (128);
          if (val[i] == NULL)
        {
          csql_Error_code = CSQL_ERR_NO_MORE_MEMORY;
          goto error;
        }

          if (iso_lvl < 1 || iso_lvl > 6)
        {
          iso_lvl = 0;
          async_ws = false;
        }

          sprintf (val[i], "%s%s", csql_Isolation_level_string[iso_lvl], (async_ws ? ", ASYNC WORKSPACE" : ""));
        }
      else if ((stmt_type == CUBRID_STMT_GET_TIMEOUT) && (db_get_float (&db_value) == -1.0))
        {
          val[i] = (char *) malloc (9);
          if (val[i] == NULL)
        {
          csql_Error_code = CSQL_ERR_NO_MORE_MEMORY;
          goto error;
        }
          strcpy (val[i], "INFINITE");
        }
      else
        {
          char *temp;

          temp = csql_db_value_as_string (&db_value, &len[i], csql_arg);
          if (temp == NULL)
        {
          csql_Error_code = CSQL_ERR_NO_MORE_MEMORY;
          goto error;
        }
          temp[len[i]] = '\0';
          val[i] = temp;
        }
    }

      if (len[i] == 0 && val[i])
    {
      len[i] = strlen (val[i]);
    }

      if (db_value.need_clear)
    {
      pr_clear_value (&db_value);
    }
    }

  if (lengths)
    {
      *lengths = len;
    }
  return (val);

error:
  if (val != NULL)
    {
      for (i = 0; i < num_attrs; i++)
    {
      if (val[i] != NULL)
        {
          free_and_init (val[i]);
        }
    }
      free_and_init (val);
    }
  if (len != NULL)
    {
      free_and_init (len);
    }
  if (db_value.need_clear)
    {
      pr_clear_value (&db_value);
    }
  return ((char **) NULL);
}

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

static void (*csql_pipe_save) (int sig);
/*
 * write_results_to_stream()
 *   return: CSQL_FAILURE/CSQL_SUCCESS
 *   csql_arg(in): csql argument
 *   fp(in): file stream pointer
 *   result_info(in): pointer to current query result info structure
 *
 * Note: This function may set csql_Error_code CSQL_ERR_SQL_ERROR to indicate
 *       the error
 */
static int
write_results_to_stream (const CSQL_ARGUMENT * csql_arg, FILE * fp, const CUR_RESULT_INFO * result_info)
{
  /*
   * These are volatile to avoid dangerous interaction with the longjmp
   * handler for SIGPIPE problems.  The typedef is necessary so that we
   * can tell the compiler that the top POINTER is volatile, not the
   * characters that it eventually points to.
   */
  typedef char **value_array;
  volatile value_array val; /* attribute values array */
  volatile int error;       /* to switch return of CSQL_FAILURE/CSQL_SUCCESS */
  int i;            /* loop counter */
  int object_no;        /* result object count */
  int e;            /* error code from DBI */
  FILE *pf;         /* pipe stream to pager */
  int n;            /* # of cols for a line */
  CUBRID_STMT_TYPE stmt_type = result_info->curr_stmt_type;
  DB_QUERY_RESULT *result = result_info->query_result;
  DB_TYPE *attr_types = result_info->attr_types;
  int line_no = result_info->curr_stmt_line_no;
  int num_attrs = result_info->num_attrs;
  int *attr_lengths = result_info->attr_lengths;
  char **attr_names = result_info->attr_names;
  char *refined_attr_name = NULL;
  char *value = NULL;
  int max_attr_name_length = result_info->max_attr_name_length;
  int column_width;
  int csql_string_width = csql_arg->string_width;
  char csql_column_delimiter;
  int value_width;
  bool is_null;

  val = (char **) NULL;
  error = FALSE;

  /*
   * Do this *before* the setjmp to avoid the possibility of the value
   * being clobbered by a longjmp.  Even if some internal thing longjmps
   * to the end of the next block we still need to be able to close the
   * pipe, so we can't risk having pf set back to some unknown value.
   */
  pf = csql_popen (csql_Pager_cmd, fp);

  if (setjmp (csql_Jmp_buf) == 0)
    {
#if !defined(WINDOWS)
      csql_pipe_save = os_set_signal_handler (SIGPIPE, &csql_pipe_handler);
#endif /* !WINDOWS */

      if (csql_arg->plain_output == false && csql_arg->query_output == false && csql_arg->loaddb_output == false)
    {
      csql_fputs ("\n=== ", pf);
      snprintf (csql_Scratch_text, SCRATCH_TEXT_LEN, csql_get_message (CSQL_RESULT_STMT_TITLE_FORMAT),
            csql_cmd_string (stmt_type, "UNKNOWN"), line_no);
      csql_fputs (csql_Scratch_text, pf);
      csql_fputs (" ===\n\n", pf);
    }

      if (db_query_first_tuple (result) < 0)
    {
      csql_Error_code = CSQL_ERR_SQL_ERROR;
      error = TRUE;
    }
      else
    {
      if (csql_arg->skip_column_names == true || csql_arg->line_output == true)
        {
          ;
        }
      else if (csql_arg->plain_output == true || csql_arg->query_output == true)
        {
          csql_column_delimiter = (csql_arg->query_output == true) ? csql_arg->column_delimiter : '\t';

          for (i = 0; i < num_attrs; i++)
        {
          refined_attr_name = csql_string_to_plain_string (attr_names[i], strlen (attr_names[i]), NULL);
          if (refined_attr_name != NULL)
            {
              fprintf (pf, "%s", refined_attr_name);
              free_and_init (refined_attr_name);
            }
          else
            {
              fprintf (pf, "UNKNOWN");
            }

          if (i == num_attrs - 1)
            {
              fprintf (pf, "\n");
            }
          else
            {
              fprintf (pf, "%c", csql_column_delimiter);
            }
        }
        }
      else if (csql_arg->loaddb_output == true)
        {
          fprintf (pf, "%%class [ ] (");
          for (i = 0; i < num_attrs; i++)
        {
          refined_attr_name = csql_string_to_plain_string (attr_names[i], strlen (attr_names[i]), NULL);
          if (refined_attr_name != NULL)
            {
              fprintf (pf, "[%s]", refined_attr_name);
              free_and_init (refined_attr_name);
            }
          else
            {
              fprintf (pf, "UNKNOWN");
            }

          if (i == num_attrs - 1)
            {
              fprintf (pf, ")\n");
            }
          else
            {
              fprintf (pf, " ");
            }
        }
        }
      else
        {
          for (n = i = 0; i < num_attrs; i++)
        {
          fprintf (pf, "  %*s", (int) (attr_lengths[i]), attr_names[i]);
          n += 2 + ((attr_lengths[i] > 0) ? attr_lengths[i] : -attr_lengths[i]);
        }
          putc ('\n', pf);
          for (; n > 0; n--)
        {
          putc ('=', pf);
        }
          putc ('\n', pf);
        }

      for (object_no = 1;; object_no++)
        {
          csql_Row_count = object_no;
          /* free previous result */
          if (val != NULL)
        {
          for (i = 0; i < num_attrs; i++)
            {
              free_and_init (val[i]);
            }
          free_and_init (val);
        }
          int *len = NULL;

          val = get_current_result (&len, result_info, csql_arg);
          if (val == NULL)
        {
          csql_Error_code = CSQL_ERR_SQL_ERROR;
          error = TRUE;
          if (len != NULL)
            {
              free (len);
            }
          break;
        }

          if (csql_arg->line_output == true)
        {
          fprintf (pf, "<%05d>", object_no);
          for (i = 0; i < num_attrs; i++)
            {
              fprintf (pf, "%*c", (int) ((i == 0) ? 1 : 8), ' ');
              fprintf (pf, "%*s: %s\n", (int) (-max_attr_name_length), attr_names[i], val[i]);
            }
          /* fflush(pf); */
        }
          else if (csql_arg->plain_output == true)
        {
          for (i = 0; i < num_attrs - 1; i++)
            {
              fprintf (pf, "%s\t", val[i]);
            }
          fprintf (pf, "%s\n", val[i]);
        }
          else if (csql_arg->query_output == true || csql_arg->loaddb_output == true)
        {
          for (i = 0; i < num_attrs - 1; i++)
            {
              fprintf (pf, "%s%c", val[i], csql_arg->column_delimiter);
            }
          fprintf (pf, "%s\n", val[i]);
        }
          else
        {
          int padding_size;

          for (i = 0; i < num_attrs; i++)
            {
              if (strcmp ("NULL", val[i]) == 0)
            {
              is_null = true;
            }
              else
            {
              is_null = false;
            }

              column_width = csql_get_column_width (attr_names[i]);
              value_width = calculate_width (column_width, csql_string_width, len[i], attr_types[i], is_null);

              padding_size =
            (attr_lengths[i] > 0) ? MAX (attr_lengths[i] - (value_width),
                             0) : MIN (attr_lengths[i] + (value_width), 0);

              fprintf (pf, "  ");
              if (padding_size > 0)
            {
              /* right justified */
              fprintf (pf, "%*s", (int) padding_size, "");
            }

              value = val[i];
              if (is_type_that_has_suffix (attr_types[i]) && is_null == false)
            {
              value[value_width - 1] = '\'';
            }

              fwrite (value, 1, value_width, pf);

              if (padding_size < 0)
            {
              /* left justified */
              fprintf (pf, "%*s", (int) (-padding_size), "");
            }
            }
          putc ('\n', pf);
          /* fflush(pf); */
        }
          if (len != NULL)
        {
          free (len);
        }

          /* advance to next */
          e = db_query_next_tuple (result);
          if (e < 0)
        {
          csql_Error_code = CSQL_ERR_SQL_ERROR;
          error = TRUE;
          break;
        }
          else if (e == DB_CURSOR_END)
        {
          break;
        }
        }
      if (error != TRUE)
        {
          putc ('\n', pf);
        }
    }
    }

  if (pf)
    {
      /*
       * Don't care for a sig pipe error when closing pipe.
       *
       * NOTE if I restore to previous signal handler which could be the
       *      system default, the program could exit.
       *      I cannot use the old error handler since I could not longjmp
       */
#if !defined(WINDOWS)
      (void) os_set_signal_handler (SIGPIPE, SIG_IGN);
#endif /* !WINDOWS */
      csql_pclose (pf, fp);
    }

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

  /* free result */
  if (val != NULL)
    {
      for (i = 0; i < num_attrs; i++)
    {
      free_and_init (val[i]);
    }
      free_and_init (val);
    }

  return ((error) ? CSQL_FAILURE : CSQL_SUCCESS);
}

/*
 * calcluate_width() - calculate column's width
 *   return: width
 *   column_width(in): column width
 *   string_width(in): string width
 *   origin_width(in): real width
 *   attr_type(in): type
 *   is_null(in): check null
 */
int
calculate_width (int column_width, int string_width, int origin_width, DB_TYPE attr_type, bool is_null)
{
  int result = 0;

  if (column_width > 0)
    {
      if (is_null)
    {
      result = column_width;
    }
      else if (is_string_type (attr_type))
    {
      result = column_width + STRING_TYPE_PREFIX_SUFFIX_LENGTH;
    }
      else if (is_bit_type (attr_type))
    {
      result = column_width + BIT_TYPE_PREFIX_SUFFIX_LENGTH;
    }
      else
    {
      result = column_width;
    }
    }
  else if (is_cuttable_type_by_string_width (attr_type) && string_width > 0)
    {
      if (is_null)
    {
      result = string_width;
    }
      else if (is_string_type (attr_type))
    {
      result = string_width + STRING_TYPE_PREFIX_SUFFIX_LENGTH;
    }
      else if (is_bit_type (attr_type))
    {
      result = string_width + BIT_TYPE_PREFIX_SUFFIX_LENGTH;
    }
      else
    {
      result = string_width;
    }
    }
  else
    {
      result = origin_width;
    }

  if (result > origin_width)
    {
      result = origin_width;
    }
  if (result < 0)
    {
      result = 0;
    }

  return result;
}

/*
 * is_string_type() - check whether it is a string type or not
 *   return: bool
 *   type(in): type
 */
static bool
is_string_type (DB_TYPE type)
{
  switch (type)
    {
    case DB_TYPE_STRING:
      return true;
    case DB_TYPE_CHAR:
      return true;
    default:
      return false;
    }
  return false;
}

/*
 * is_bit_type() - check whether it is a bit type or not
 *   return: bool
 *   type(in): type
 */
static bool
is_bit_type (DB_TYPE type)
{
  switch (type)
    {
    case DB_TYPE_BIT:
      return true;
    case DB_TYPE_VARBIT:
      return true;
    default:
      return false;
    }
  return false;
}

/*
 * is_cuttable_type_by_string_width() - check whether it is cuttable type by string_width or not
 *   return: bool
 *   type(in): type
 */
static bool
is_cuttable_type_by_string_width (DB_TYPE type)
{
  switch (type)
    {
    case DB_TYPE_STRING:
    case DB_TYPE_CHAR:
    case DB_TYPE_BIT:
    case DB_TYPE_VARBIT:
      return true;
    default:
      break;
    }
  return false;
}

/*
 * is_type_that_has_suffix() - check whether this type has suffix or not
 *   return: bool
 *   type(in): type
 */
static bool
is_type_that_has_suffix (DB_TYPE type)
{
  switch (type)
    {
    case DB_TYPE_STRING:
    case DB_TYPE_CHAR:
    case DB_TYPE_BIT:
    case DB_TYPE_VARBIT:
      return true;
    default:
      break;
    }
  return false;
}

/*
 * uncontrol_strndup() - variation of strdup()
 *   return:  newly allocated string
 *   from(in): source string
 *   length(in): length of source string
 */
static char *
uncontrol_strndup (const char *from, int length)
{
  char *to;

  /* allocate memory for `to' */
  to = (char *) malloc (length + 1);
  if (to == NULL)
    {
      csql_Error_code = CSQL_ERR_NO_MORE_MEMORY;
      return ((char *) NULL);
    }

  memcpy (to, from, length);
  to[length] = 0;

  return to;
}

/*
 * uncontrol_strdup() - variation of strdup()
 *   return:  newly allocated string
 *   from(in): source string
 */
static char *
uncontrol_strdup (const char *from)
{
  return uncontrol_strndup (from, strlen (from));
}