Skip to content

File db_value_printer.cpp

File List > compat > db_value_printer.cpp

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/*
 * db_value_printer.cpp
 */

#include "db_value_printer.hpp"

#include "db_date.h"
#include "dbtype.h"
#include "memory_private_allocator.hpp"
#include "object_primitive.h"
#include "object_representation.h"
#include "printer.hpp"
#include "set_object.h"
#include "string_buffer.hpp"
#include "string_opfunc.h"
#include "tz_support.h"
#if !defined(SERVER_MODE)
#include "virtual_object.h"
#endif
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

const char db_value_printer::DECIMAL_FORMAT[] = "%#.*g";

namespace
{
  //--------------------------------------------------------------------------------
  // DB_VALUE of type DB_TYPE_BIT or DB_TYPE_VARBIT
  void describe_bit_string (string_buffer &buf, const db_value *value, bool pad_byte)
  {
    const unsigned char *bstring;
    int nibble_length, nibbles, count;

    assert (value != NULL);

    bstring = REINTERPRET_CAST (const unsigned char *, db_get_string (value));
    if (bstring == NULL)
      {
    return;
      }

    nibble_length = ((db_get_string_length (value) + 3) / 4);
    for (nibbles = 0, count = 0; nibbles < nibble_length - 1; count++, nibbles += 2)
      {
    buf ("%02x", bstring[count]);
      }

    /* If we don't have a full byte on the end, print the nibble. */
    if (nibbles < nibble_length)
      {
    if (pad_byte)
      {
        buf ("%02x", bstring[count]);
      }
    else
      {
        //use only the 1st hex digit
        char tmp[3] = {0};
        sprintf (tmp, "%1x", bstring[count]);
        buf += tmp[0];
      }
      }
  }

  //--------------------------------------------------------------------------------
  void describe_real (string_buffer &buf, double value, int precision)
  {
    char tbuf[24];

    snprintf (tbuf, sizeof (tbuf), db_value_printer::DECIMAL_FORMAT, precision, value);
    if (strstr (tbuf, "Inf"))
      {
    snprintf (tbuf, sizeof (tbuf), db_value_printer::DECIMAL_FORMAT, precision, (value > 0 ? FLT_MAX : -FLT_MAX));
      }
    buf (tbuf);
  }
}//namespace

//--------------------------------------------------------------------------------
void db_value_printer::describe_money (const db_monetary *value)
{
  assert (value != NULL);

  m_buf ("%s%.2f", intl_get_money_esc_ISO_symbol (value->type), value->amount);
  if (strstr (m_buf.get_buffer (), "Inf"))
    {
      m_buf ("%s%.2f", intl_get_money_esc_ISO_symbol (value->type), (value->amount > 0 ? DBL_MAX : -DBL_MAX));
    }
}

//--------------------------------------------------------------------------------
void db_value_printer::describe_comment_value (const db_value *value)
{
  INTL_CODESET codeset = INTL_CODESET_NONE;
  const char *src, *end;

  codeset = db_get_string_codeset (value);
  if (codeset != LANG_SYS_CODESET)
    {
      m_buf ("%s", lang_charset_introducer (codeset));
    }

  m_buf += '\'';

  src = db_get_string (value);
  end = src + db_get_string_size (value);
  m_buf.add_bytes (end - src, src);

  m_buf += '\'';
}

//--------------------------------------------------------------------------------
void db_value_printer::describe_type (const db_value *value)
{
  if (DB_IS_NULL (value))
    {
      m_buf ("NULL");
    }
  else
    {
      DB_TYPE type = DB_VALUE_TYPE (value);
      switch (type)
    {
    case DB_TYPE_NULL:
      m_buf ("NULL");
      break;
    case DB_TYPE_INTEGER:
      m_buf ("INTEGER");
      break;
    case DB_TYPE_BIGINT:
      m_buf ("BIGINT");
      break;
    case DB_TYPE_FLOAT:
      m_buf ("FLOAT");
      break;
    case DB_TYPE_DOUBLE:
      m_buf ("DOUBLE");
      break;
    case DB_TYPE_VARCHAR:
      m_buf ("VARCHAR");
      break;
    case DB_TYPE_OBJECT:
      m_buf ("OBJECT");
      break;
    case DB_TYPE_SET:
      m_buf ("SET");
      break;
    case DB_TYPE_MULTISET:
      m_buf ("MULTISET");
      break;
    case DB_TYPE_SEQUENCE:
      m_buf ("SEQUENCE");
      break;
    case DB_TYPE_BLOB:
      m_buf ("BLOB");
      break;
    case DB_TYPE_CLOB:
      m_buf ("CLOB");
      break;
    case DB_TYPE_TIME:
      m_buf ("TIME");
      break;
    case DB_TYPE_TIMESTAMP:
      m_buf ("TIMESTAMP");
      break;
    case DB_TYPE_TIMESTAMPTZ:
      m_buf ("TIMESTAMPTZ");
      break;
    case DB_TYPE_TIMESTAMPLTZ:
      m_buf ("TIMESTAMPLTZ");
      break;
    case DB_TYPE_DATETIME:
      m_buf ("DATETIME");
      break;
    case DB_TYPE_DATETIMETZ:
      m_buf ("DATETIMETZ");
      break;
    case DB_TYPE_DATETIMELTZ:
      m_buf ("DATETIMELTZ");
      break;
    case DB_TYPE_DATE:
      m_buf ("DATE");
      break;
    case DB_TYPE_MONETARY:
      m_buf ("MONETARY");
      break;
    case DB_TYPE_VARIABLE:
      m_buf ("VARIABLE");
      break;
    case DB_TYPE_SUB:
      m_buf ("SUB");
      break;
    case DB_TYPE_POINTER:
      m_buf ("POINTER");
      break;
    case DB_TYPE_ERROR:
      m_buf ("ERROR");
      break;
    case DB_TYPE_SMALLINT:
      m_buf ("SMALLINT");
      break;
    case DB_TYPE_VOBJ:
      m_buf ("VOBJ");
      break;
    case DB_TYPE_OID:
      m_buf ("OID");
      break;
    case DB_TYPE_NUMERIC:
      m_buf ("NUMERIC");
      break;
    case DB_TYPE_BIT:
      m_buf ("BIT");
      break;
    case DB_TYPE_VARBIT:
      m_buf ("VARBIT");
      break;
    case DB_TYPE_CHAR:
      m_buf ("CHAR");
      break;
    case DB_TYPE_DB_VALUE:
      m_buf ("DB_VALUE");
      break;
    case DB_TYPE_RESULTSET:
      m_buf ("DB_RESULTSET");
      break;
    case DB_TYPE_MIDXKEY:
      m_buf ("DB_MIDXKEY");
      break;
    case DB_TYPE_TABLE:
      m_buf ("DB_TABLE");
      break;
    case DB_TYPE_ENUMERATION:
      m_buf ("ENUM");
      break;
    case DB_TYPE_JSON:
      m_buf ("JSON");
      break;
    default:
      m_buf ("UNKNOWN");
      break;
    }
    }
}

//--------------------------------------------------------------------------------
void db_value_printer::describe_value (const db_value *value)
{
  INTL_CODESET codeset = INTL_CODESET_NONE;

  if (DB_IS_NULL (value))
    {
      m_buf ("NULL");
    }
  else
    {
      /* add some extra info to the basic data value */
      switch (DB_VALUE_TYPE (value))
    {
    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      codeset = db_get_string_codeset (value);
      if (codeset != LANG_SYS_CODESET)
        {
          m_buf ("%s", lang_charset_introducer (codeset));
        }
      m_buf += '\'';
      describe_data (value);
      m_buf += '\'';
      break;

    case DB_TYPE_ENUMERATION:
      if (db_get_enum_string (value) == NULL && db_get_enum_short (value) != 0)
        {
          /* to print enum index as int */
          m_buf ("%d", (int)db_get_enum_short (value));
          break;
        }
      else
        {
          DB_VALUE varchar_val;

          /* print enumerations as strings */
          if (tp_enumeration_to_varchar (value, &varchar_val) == NO_ERROR)
        {
          codeset = (INTL_CODESET) db_get_enum_codeset (value);
          if (codeset != LANG_SYS_CODESET)
            {
              m_buf ("%s", lang_charset_introducer (codeset));
            }
          describe_value (&varchar_val);
        }
          else
        {
          /* tp_enumeration_to_varchar only fails if the enum string is null which we already checked */
          assert (false);
        }
        }
      break;
    case DB_TYPE_DATE:
      m_buf ("date '");
      describe_data (value);
      m_buf += '\'';
      break;

    case DB_TYPE_JSON:
      m_buf ("json '");
      describe_data (value);
      m_buf += '\'';
      break;

    case DB_TYPE_TIME:
      m_buf ("time '");
      describe_data (value);
      m_buf += '\'';
      break;
    case DB_TYPE_TIMESTAMP:
      m_buf ("timestamp '");
      describe_data (value);
      m_buf += '\'';
      break;
    case DB_TYPE_TIMESTAMPTZ:
      m_buf ("timestamptz '");
      describe_data (value);
      m_buf += '\'';
      break;
    case DB_TYPE_TIMESTAMPLTZ:
      m_buf ("timestampltz '");
      describe_data (value);
      m_buf += '\'';
      break;
    case DB_TYPE_DATETIME:
      m_buf ("datetime '");
      describe_data (value);
      m_buf += '\'';
      break;
    case DB_TYPE_DATETIMETZ:
      m_buf ("datetimetz '");
      describe_data (value);
      m_buf += '\'';
      break;
    case DB_TYPE_DATETIMELTZ:
      m_buf ("datetimeltz '");
      describe_data (value);
      m_buf += '\'';
      break;
    case DB_TYPE_BIT:
    case DB_TYPE_VARBIT:
      m_buf ("X'");
      describe_data (value);
      m_buf += '\'';
      break;
    case DB_TYPE_BLOB:
      m_buf ("BLOB'");
      describe_data (value);
      m_buf += '\'';
      break;
    case DB_TYPE_CLOB:
      m_buf ("CLOB'");
      describe_data (value);
      m_buf += '\'';
      break;
    default:
      describe_data (value);
      break;
    }
    }
}

//--------------------------------------------------------------------------------
void db_value_printer::describe_data (const db_value *value)
{
  OID         *oid = 0;
  db_object   *obj = 0;
  db_monetary *money = 0;
  DB_SET      *set = 0;
  db_elo      *elo = 0;
  DB_MIDXKEY *midxkey;
  const char *src, *pos, *end;
  double d;
  char line[1025];
  char *json_body = NULL;

  if (DB_IS_NULL (value))
    {
      m_buf ("NULL");
    }
  switch (DB_VALUE_TYPE (value))
    {
    case DB_TYPE_INTEGER:
      m_buf ("%d", db_get_int (value));
      break;

    case DB_TYPE_BIGINT:
      m_buf ("%lld", (long long) db_get_bigint (value));
      break;

    case DB_TYPE_POINTER:
      m_buf ("%p", db_get_pointer (value));
      break;

    case DB_TYPE_SHORT:
      m_buf ("%d", (int) db_get_short (value));
      break;

    case DB_TYPE_ERROR:
      m_buf ("%d", (int) db_get_error (value));
      break;

    case DB_TYPE_FLOAT:
      m_buf ("%f", (double) db_get_float (value));
      break;

    case DB_TYPE_DOUBLE:
      m_buf ("%e", (double) db_get_double (value));
      break;

    case DB_TYPE_NUMERIC:
      m_buf ("%s", numeric_db_value_print (value, line));
      break;

    case DB_TYPE_BIT:
    case DB_TYPE_VARBIT:
      describe_bit_string (m_buf, value, m_padding);
      break;

    case DB_TYPE_CHAR:
    case DB_TYPE_VARCHAR:
      /* Copy string into buf providing for any embedded quotes. Strings may have embedded NULL characters and
       * embedded quotes.  None of the supported multibyte character codesets have a conflict between a quote
       * character and the second byte of the multibyte character.
       */
      src = db_get_string (value);
      end = src + db_get_string_size (value);
      while (src < end)
    {
      /* Find the position of the next quote or the end of the string, whichever comes first.  This loop is
       * done in place of strchr in case the string has an embedded NULL.
       */
      for (pos = src; pos && pos < end && (*pos) != '\''; pos++)
        ;

      /* If pos < end, then a quote was found.  If so, copy the partial buffer and duplicate the quote */
      if (pos < end)
        {
          m_buf.add_bytes (pos - src + 1, src);
          m_buf += '\'';
        }
      /* If not, copy the remaining part of the buffer */
      else
        {
          m_buf.add_bytes (end - src, src);
        }

      /* advance src to just beyond the point where we left off */
      src = pos + 1;
    }
      break;

    case DB_TYPE_OBJECT:
#if defined(SERVER_MODE)
      assert (false);
#else //#if defined(SERVER_MODE)
      obj = db_get_object (value);
      if (obj == NULL)
    {
      break;
    }
      if (obj->is_vid)
    {
      DB_VALUE vobj;
      vid_object_to_vobj (obj, &vobj);
      describe_value (&vobj);
      break;
    }
      oid = WS_OID (obj);
      m_buf ("%d|%d|%d", int (oid->volid), int (oid->pageid), int (oid->slotid));
#endif //#if defined(SERVER_MODE)
      break;
    /* If we are on the server, fall thru to the oid case The value is probably nonsense, but that is safe to do.
     * This case should simply not occur.
     */

    case DB_TYPE_OID:
      oid = (OID *) db_get_oid (value);
      if (oid == NULL)
    {
      break;
    }
      m_buf ("%d|%d|%d", int (oid->volid), int (oid->pageid), int (oid->slotid));
      break;

    case DB_TYPE_VOBJ:
      m_buf ("vid:");
      [[fallthrough]];
    case DB_TYPE_SET:
    case DB_TYPE_MULTISET:
    case DB_TYPE_SEQUENCE:
      set = db_get_set (value);
      if (set != NULL)
    {
      describe_set (set);
    }
      else
    {
      m_buf ("NULL");
    }
      break;

    case DB_TYPE_JSON:
      json_body = db_get_json_raw_body (value);
      m_buf ("%s", json_body);
      db_private_free (NULL, json_body);
      break;

    case DB_TYPE_MIDXKEY:
      midxkey = db_get_midxkey (value);
      if (midxkey != NULL)
    {
      describe_midxkey (midxkey);
    }
      else
    {
      m_buf ("NULL");
    }
      break;

    case DB_TYPE_BLOB:
    case DB_TYPE_CLOB:
      elo = db_get_elo (value);
      if (elo != NULL)
    {
      if (elo->type == ELO_FBO)
        {
          assert (elo->locator != NULL);
          m_buf ("%s", elo->locator);
        }
      else        /* ELO_LO */
        {
          /* should not happen for now */
          assert (0);
        }
    }
      else
    {
      m_buf ("NULL");
    }
      break;

      /*
       * This constant is necessary to fake out the db_?_to_string()
       * routines that are expecting a buffer length.  Since we assume
       * that our buffer is big enough in this code, just pass something
       * that ought to work for every case.
       */
#define TOO_BIG_TO_MATTER       1024

    case DB_TYPE_TIME:
      (void) db_time_to_string (line, TOO_BIG_TO_MATTER, db_get_time (value));
      m_buf (line);
      break;

    case DB_TYPE_TIMESTAMP:
      (void) db_utime_to_string (line, TOO_BIG_TO_MATTER, db_get_timestamp (value));
      m_buf (line);
      break;

    case DB_TYPE_TIMESTAMPLTZ:
      (void) db_timestampltz_to_string (line, TOO_BIG_TO_MATTER, db_get_timestamp (value));
      m_buf (line);
      break;

    case DB_TYPE_TIMESTAMPTZ:
    {
      DB_TIMESTAMPTZ *ts_tz;

      ts_tz = db_get_timestamptz (value);
      (void) db_timestamptz_to_string (line, TOO_BIG_TO_MATTER, & (ts_tz->timestamp), & (ts_tz->tz_id));
      m_buf (line);
    }
    break;

    case DB_TYPE_DATETIME:
      (void) db_datetime_to_string (line, TOO_BIG_TO_MATTER, db_get_datetime (value));
      m_buf (line);
      break;
    case DB_TYPE_DATETIMELTZ:
      (void) db_datetimeltz_to_string (line, TOO_BIG_TO_MATTER, db_get_datetime (value));
      m_buf (line);
      break;

    case DB_TYPE_DATETIMETZ:
    {
      DB_DATETIMETZ *dt_tz;

      dt_tz = db_get_datetimetz (value);
      (void) db_datetimetz_to_string (line, TOO_BIG_TO_MATTER, & (dt_tz->datetime), & (dt_tz->tz_id));
      m_buf (line);
    }
    break;

    case DB_TYPE_DATE:
      (void) db_date_to_string (line, TOO_BIG_TO_MATTER, db_get_date (value));
      m_buf (line);
      break;

    case DB_TYPE_MONETARY:
      money = db_get_monetary (value);
      OR_MOVE_DOUBLE (&money->amount, &d);
      describe_money (money);
      break;

    case DB_TYPE_NULL:
      /* Can't get here because the DB_IS_NULL test covers DB_TYPE_NULL */
      break;

    case DB_TYPE_VARIABLE:
    case DB_TYPE_SUB:
    case DB_TYPE_DB_VALUE:
      /* make sure line is NULL terminated, may not be necessary line[0] = '\0'; */
      break;

    default:
      /* NB: THERE MUST BE NO DEFAULT CASE HERE. ALL TYPES MUST BE HANDLED! */
      assert (false);
      break;
    }
}

//--------------------------------------------------------------------------------
void db_value_printer::describe_midxkey (const db_midxkey *midxkey, int help_Max_set_elements)
{
  db_value value;
  int size, end, i;
  int prev_i_index;
  char *prev_i_ptr;

  assert (midxkey != NULL);

  m_buf += '{';
  size = midxkey->ncolumns;
  if (help_Max_set_elements == 0 || help_Max_set_elements > size)
    {
      end = size;
    }
  else
    {
      end = help_Max_set_elements;
    }

  prev_i_index = 0;
  prev_i_ptr = NULL;
  for (i = 0; i < end; i++)
    {
      pr_midxkey_get_element_nocopy (midxkey, i, &value, &prev_i_index, &prev_i_ptr);
      describe_value (&value);
      if (i < size - 1)
    {
      m_buf (", ");
    }
      if (!DB_IS_NULL (&value) && value.need_clear == true)
    {
      pr_clear_value (&value);
    }
    }
  if (i < size)
    {
      m_buf (". . .");
    }
  m_buf += '}';
}

//--------------------------------------------------------------------------------
void db_value_printer::describe_set (const db_set *set, int help_Max_set_elements)
{
  DB_VALUE value;
  int size, end, i;

  assert (set != NULL);

  m_buf += '{';
  size = set_size ((DB_COLLECTION *)set);
  if (help_Max_set_elements == 0 || help_Max_set_elements > size)
    {
      end = size;
    }
  else
    {
      end = help_Max_set_elements;
    }

  for (i = 0; i < end; ++i)
    {
      set_get_element ((DB_COLLECTION *)set, i, &value);
      describe_value (&value);
      db_value_clear (&value);
      if (i < size - 1)
    {
      m_buf (", ");
    }
    }
  if (i < size)
    {
      m_buf (". . .");
    }
  m_buf += '}';
}

/*
 * db_value_fprint() -  Prints a description of the contents of a DB_VALUE
 *                        to the file
 *   return: none
 *   fp(in) : FILE stream pointer
 *   value(in) : value to print
 */
void
db_fprint_value (FILE *fp, const db_value *value)
{
  const size_t BUFFER_SIZE = 1024;
  string_buffer sb (cubmem::PRIVATE_BLOCK_ALLOCATOR, BUFFER_SIZE);

  db_value_printer printer (sb);
  printer.describe_value (value);
  fprintf (fp, "%.*s", (int) sb.len (), sb.get_buffer ());
}

/*
 * db_print_value() -  Prints a description of the contents of a DB_VALUE
 *                        to the file
 *   return: none
 *   fp(in) : FILE stream pointer
 *   value(in) : value to print
 */
void
db_print_value (print_output &output_ctx, const db_value *value)
{
  string_buffer *p_sb;

  /* TODO : change 'db_value_printer' to use print_output instead of string_buffer */
  p_sb = output_ctx.grab_string_buffer ();

  if (p_sb != NULL)
    {
      db_value_printer printer (*p_sb);
      printer.describe_value (value);
    }
  else
    {
      const size_t BUFFER_SIZE = 1024;
      string_buffer sb (cubmem::PRIVATE_BLOCK_ALLOCATOR, BUFFER_SIZE);

      db_value_printer printer (sb);
      printer.describe_value (value);
      output_ctx ("%.*s", (int) sb.len (), sb.get_buffer ());
    }
}

/*
 * db_sprint_value() - This places a printed representation of the supplied value in a buffer.
 *   value(in) : value to describe
 *   sb(in/out) : auto resizable buffer to contain description
 */
void
db_sprint_value (const db_value *value, string_buffer &sb)
{
  db_value_printer printer (sb);
  printer.describe_value (value);
}

#ifndef NDEBUG
void
db_value_print_console (const db_value *value, bool add_newline, char *fmt, ...)
{
  if (fmt && *fmt)
    {
      va_list ap;

      va_start (ap, fmt);
      (void) vfprintf (stdout, fmt, ap);
      va_end (ap);
    }

  if (value)
    {
      db_fprint_value (stdout, value);
    }

  if (add_newline)
    {
      fprintf (stdout, "\n");
    }

  fflush (stdout);
}
#endif