Skip to content

File flashback_cl.c

File List > cubrid > src > transaction > flashback_cl.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.
 *
 */

/*
 * flashback_cl.c -
 */

#ident "$Id$"

#if !defined(WINDOWS)
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#endif

#include "config.h"

#include <stdio.h>
#if defined(WINDOWS)
#include <windows.h>
#include <conio.h>
#else /* WINDOWS */
#include <sys/ioctl.h>
#include <unistd.h>
#include <termios.h>
#endif /* LINUX */

#include "flashback_cl.h"
#include "object_representation.h"
#include "object_primitive.h"
#include "message_catalog.h"
#include "schema_manager.h"
#include "authenticate.h"
#include "utility.h"
#include "csql.h"
#include "system_parameter.h"

typedef enum
{
  FLASHBACK_INSERT = 0,
  FLASHBACK_UPDATE,
  FLASHBACK_DELETE
} FLASHBACK_DML_TYPE;

#if defined(WINDOWS)
static int
flashback_util_get_winsize ()
{
  CONSOLE_SCREEN_BUFFER_INFO csbi;

  /* if window size is set for the test purpose, then use it */

  int window_size = prm_get_integer_value (PRM_ID_FLASHBACK_WIN_SIZE);
  if (window_size > 0)
    {
      return window_size;
    }
  else
    {
      GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &csbi);

      return csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
    }
}

static char
flashback_util_get_char ()
{
  int c;

  c = getche ();

  return c;
}

#else
static char
flashback_util_get_char ()
{
  int c;
  struct termios oldt, newt;

  tcgetattr (STDIN_FILENO, &oldt);
  newt = oldt;

  newt.c_lflag &= ~(ICANON);
  tcsetattr (STDIN_FILENO, TCSANOW, &newt);

  c = getchar ();
  tcsetattr (STDIN_FILENO, TCSANOW, &oldt);

  return c;
}

static int
flashback_util_get_winsize ()
{
  struct winsize w;

  /* if window size is set for test purpose, then use it */

  int window_size = prm_get_integer_value (PRM_ID_FLASHBACK_WIN_SIZE);
  if (window_size > 0)
    {
      return window_size;
    }
  else
    {
      ioctl (STDOUT_FILENO, TIOCGWINSZ, &w);

      return w.ws_row;
    }
}
#endif

/*
 * flashback_find_class_index () - find the index of oidlist which contains classoid
 *
 * return              : class index
 * oidlist (in)        : OID of classnames
 * list_size (in)      : size of oidlist
 * classoid (in)       : classoid to find
 */

int
flashback_find_class_index (OID * oidlist, int list_size, OID classoid)
{
  int class_index = 0;

  for (class_index = 0; class_index < list_size; class_index++)
    {
      if (OID_EQ (&oidlist[class_index], &classoid))
    {
      return class_index;
    }
    }

  return -1;
}

/*
 * flashback_unpack_and_print_summary () 
 *
 * return                   : error code
 * summary_buffer (in/out)  : input buffer which contains summary info, and return advanced buffer pointer
 * summary (out)            : brief summary information
 * num_summary (in)         : number of summary to unpack
 * classname_list (in)      : classnames
 * oidlist (in)             : OID of classnames
 */

int
flashback_unpack_and_print_summary (char **summary_buffer, FLASHBACK_SUMMARY_INFO_MAP * summary,
                    dynamic_array * classname_list, OID * oidlist)
{
  time_t summary_start_time = 0;
  time_t summary_end_time = 0;
  int num_summary = 0;

  TRANID trid;
  char *user = NULL;
  time_t start_time, end_time;
  int num_insert, num_update, num_delete;
  LOG_LSA start_lsa, end_lsa;
  int num_table;
  OID classoid;
  char *tmp_ptr = *summary_buffer;

  bool stop_print = false;
  const int max_window_size = flashback_util_get_winsize ();
  int line_cnt = 0;

  char stime_buf[20];
  char etime_buf[20];

  tmp_ptr = or_unpack_int64 (tmp_ptr, &summary_start_time);
  tmp_ptr = or_unpack_int64 (tmp_ptr, &summary_end_time);

  tmp_ptr = or_unpack_int (tmp_ptr, &num_summary);
  if (num_summary == 0)
    {
      printf ("There is nothing to flashback\n");

      return NO_ERROR;
    }


  strftime (stime_buf, 20, "%d-%m-%Y:%H:%M:%S", localtime (&summary_start_time));
  strftime (etime_buf, 20, "%d-%m-%Y:%H:%M:%S", localtime (&summary_end_time));

  printf ("Flashback Summary\n");
  printf ("Number of Transaction: %d\n", num_summary);
  printf ("Start date - End date:%20s -%20s\n", stime_buf, etime_buf);

  line_cnt = line_cnt + 3;

  printf
    ("Transaction id  User name                         Start time            End time              Num_insert  Num_update  Num_delete  Table\n");
  line_cnt++;

  for (int i = 0; i < num_summary; i++)
    {
      FLASHBACK_SUMMARY_INFO info = { 0, LSA_INITIALIZER, LSA_INITIALIZER };

      tmp_ptr = or_unpack_int (tmp_ptr, &trid);
      tmp_ptr = or_unpack_string_nocopy (tmp_ptr, &user);
      tmp_ptr = or_unpack_int64 (tmp_ptr, &start_time);
      tmp_ptr = or_unpack_int64 (tmp_ptr, &end_time);
      tmp_ptr = or_unpack_int (tmp_ptr, &num_insert);
      tmp_ptr = or_unpack_int (tmp_ptr, &num_update);
      tmp_ptr = or_unpack_int (tmp_ptr, &num_delete);
      tmp_ptr = or_unpack_log_lsa (tmp_ptr, &start_lsa);
      tmp_ptr = or_unpack_log_lsa (tmp_ptr, &end_lsa);
      tmp_ptr = or_unpack_int (tmp_ptr, &num_table);

      info.trid = trid;
      info.start_lsa = start_lsa;
      info.end_lsa = end_lsa;
      strcpy (info.user, user);

      summary->emplace (trid, info);

      if (!stop_print)
    {
      if (start_time != 0)
        {
          strftime (stime_buf, 20, "%d-%m-%Y:%H:%M:%S", localtime (&start_time));
        }

      if (end_time != 0)
        {
          strftime (etime_buf, 20, "%d-%m-%Y:%H:%M:%S", localtime (&end_time));
        }

      printf ("%14d  ", trid);
      printf ("%-32s  ", user);
      printf ("%-20s  ", start_time == 0 ? "-" : stime_buf);
      printf ("%-20s  ", end_time == 0 ? "-" : etime_buf);

      printf ("%10d  ", num_insert);
      printf ("%10d  ", num_update);
      printf ("%10d  ", num_delete);
    }

      for (int j = 0; j < num_table; j++)
    {
      int idx = 0;
      char classname[SM_MAX_IDENTIFIER_LENGTH + 1] = "\0";

      tmp_ptr = or_unpack_oid (tmp_ptr, &classoid);
      if (!stop_print)
        {
          idx = flashback_find_class_index (oidlist, da_size (classname_list), classoid);
          if (idx == -1)
        {
          /* er_set */
          return ER_FLASHBACK_INVALID_CLASS;
        }

          da_get (classname_list, idx, classname);
          if (j == 0)
        {
          printf ("%-s\n", classname);
          line_cnt++;
        }
          else
        {
          printf ("%130s%-s\n", "", classname);
          line_cnt++;
        }
        }
    }

      if (line_cnt >= max_window_size)
    {
      char c;
      printf ("press 'q' to quit or press anything to continue");

      c = flashback_util_get_char ();
      if (c == 'q')
        {
          stop_print = true;
        }

      line_cnt = 0;

      /* Remove the message above, because above message is no more required */
      printf ("\33[2K\r");
    }
    }

  *summary_buffer = tmp_ptr;

  return NO_ERROR;
}

static int
flashback_check_and_resize_sql_memory (char **sql, int req_size, int *max_sql_size)
{
  char *tmp = NULL;

  if (req_size <= *max_sql_size)
    {
      return NO_ERROR;
    }

  while (*max_sql_size < req_size)
    {
      *max_sql_size = *max_sql_size * 2;
    }

  tmp = (char *) realloc (*sql, *max_sql_size);

  if (tmp)
    {
      *sql = tmp;
    }
  else
    {
      util_log_write_errid (MSGCAT_UTIL_GENERIC_NO_MEM);
      return ER_OUT_OF_VIRTUAL_MEMORY;
    }

  return NO_ERROR;
}

#define IS_STRING_TYPE(type)     (type == DB_TYPE_STRING || \
                                  type == DB_TYPE_CHAR)

#define IS_QOUTES_NEEDED(type)   (type == DB_TYPE_TIME || \
                                  type == DB_TYPE_TIMESTAMP || \
                                  type == DB_TYPE_TIMESTAMPTZ || \
                                  type == DB_TYPE_TIMESTAMPLTZ || \
                                  type == DB_TYPE_DATE || \
                                  type == DB_TYPE_DATETIME || \
                                  type == DB_TYPE_DATETIMETZ || \
                                  type == DB_TYPE_DATETIMELTZ)

typedef enum
{
  PACK_INT = 0,
  PACK_INT64 = 1,
  PACK_FLOAT = 2,
  PACK_DOUBLE = 3,
  PACK_SHORT = 4,
  PACK_STRING = 7
} FLASHBACK_PACK_FUNC_TYPE;

static int
flashback_process_column_data (char **data, char **sql, int *max_sql_size, DB_TYPE type, bool is_condition_column)
{
  char *ptr = *data;

  int error = NO_ERROR;

  int sql_length = strlen (*sql);

  FLASHBACK_PACK_FUNC_TYPE func_type;

  int i_data;
  double d_data;
  float f_data;
  short sh_data;
  char *s_data;
  INT64 b_data;

  /* maximum length of the string converted from a decimal
   * INT64 represents maximum of 9223372036854775807, and is 20 characters long */
  const int max_decimal_digits_len = 20;

  ptr = or_unpack_int (ptr, (int *) &func_type);

  switch (func_type)
    {
    case PACK_INT:
      ptr = or_unpack_int (ptr, &i_data);
      error = flashback_check_and_resize_sql_memory (sql, sql_length + max_decimal_digits_len, max_sql_size);
      if (error != NO_ERROR)
    {
      return error;
    }

      sprintf (*sql + sql_length, "%d", i_data);
      break;
    case PACK_INT64:
      ptr = or_unpack_int64 (ptr, &b_data);

      error = flashback_check_and_resize_sql_memory (sql, sql_length + max_decimal_digits_len, max_sql_size);
      if (error != NO_ERROR)
    {
      return error;
    }

      sprintf (*sql + sql_length, "%" PRId64, b_data);
      break;
    case PACK_FLOAT:
      ptr = or_unpack_float (ptr, &f_data);
      error = flashback_check_and_resize_sql_memory (sql, sql_length + max_decimal_digits_len, max_sql_size);
      if (error != NO_ERROR)
    {
      return error;
    }

      sprintf (*sql + sql_length, "%f", f_data);
      break;
    case PACK_DOUBLE:
      ptr = or_unpack_double (ptr, &d_data);
      error = flashback_check_and_resize_sql_memory (sql, sql_length + max_decimal_digits_len, max_sql_size);
      if (error != NO_ERROR)
    {
      return error;
    }

      sprintf (*sql + sql_length, "%lf", d_data);
      break;
    case PACK_SHORT:
      ptr = or_unpack_short (ptr, &sh_data);
      error = flashback_check_and_resize_sql_memory (sql, sql_length + max_decimal_digits_len, max_sql_size);
      if (error != NO_ERROR)
    {
      return error;
    }

      sprintf (*sql + sql_length, "%d", sh_data);
      break;
    case PACK_STRING:
      ptr = or_unpack_string_nocopy (ptr, &s_data);

      if (s_data == NULL)
    {
      if (is_condition_column == true)
        {
          /* Add the length of the string 'is null' and substract the length of '= ' */
          error = flashback_check_and_resize_sql_memory (sql, sql_length + 5, max_sql_size);
          if (error != NO_ERROR)
        {
          return error;
        }

          /* To remove the '= ' string which was attached by the caller */
          sprintf (*sql + sql_length - 2, "is null");
        }
      else
        {
          /* Add the length of the string 'null' */
          error = flashback_check_and_resize_sql_memory (sql, sql_length + 4, max_sql_size);
          if (error != NO_ERROR)
        {
          return error;
        }

          strcat (*sql, "null");
        }
    }
      else if (IS_STRING_TYPE (type))
    {
      int result_length = 0;
      char *result_string = NULL;

      result_string = string_to_string (s_data, '\'', '\0', strlen (s_data), &result_length, false, true);
      if (result_string == NULL)
        {
          /* internally stirng_to_string() allocates memory for string */
          util_log_write_errid (MSGCAT_UTIL_GENERIC_NO_MEM);
          return ER_OUT_OF_VIRTUAL_MEMORY;
        }

      error = flashback_check_and_resize_sql_memory (sql, sql_length + result_length + 1, max_sql_size);
      if (error != NO_ERROR)
        {
          free_and_init (result_string);

          return error;
        }

      sprintf (*sql + sql_length, "%s", result_string);

      free_and_init (result_string);
    }
      else if (IS_QOUTES_NEEDED (type))
    {
      error = flashback_check_and_resize_sql_memory (sql, sql_length + strlen (s_data) + 3, max_sql_size);
      if (error != NO_ERROR)
        {
          return error;
        }

      sprintf (*sql + sql_length, "\'%s\'", s_data);
    }
      else
    {
      error = flashback_check_and_resize_sql_memory (sql, sql_length + strlen (s_data) + 1, max_sql_size);
      if (error != NO_ERROR)
        {
          return error;
        }

      sprintf (*sql + sql_length, "%s", s_data);
    }

      break;
    default:
      break;
    }

  *data = ptr;

  return NO_ERROR;
}

static void
flashback_print_detail (int trid, char *user, char *flashback, char *original, FILE * outfp)
{
  fprintf (outfp, "[TRANSACTION ID] %d \n", trid);
  fprintf (outfp, "[USER]           %-s \n", user);
  fprintf (outfp, "[ORIGINAL]       %-s \n", original);
  fprintf (outfp, "[FLASHBACK]      %-s \n", flashback);
}

static int
flashback_print_update (char **loginfo, int trid, char *user, const char *classname, bool is_detail, FILE * outfp)
{
  MOP classop;
  SM_CLASS *class_;
  SM_ATTRIBUTE *attr;

  int error = NO_ERROR;

  char *sql = NULL;
  int max_sql_size = 1024;

  char *cond_sql = NULL;
  int max_cond_sql_size = 1024;

  char *original_sql = NULL;
  int max_original_size = 1024;

  char *start_ptr;
  char *ptr;

  int num_change_col;
  int change_index;

  int num_cond_col;
  int cond_index;

  int i = 0;

  sql = (char *) malloc (max_sql_size);
  if (sql == NULL)
    {
      util_log_write_errid (MSGCAT_UTIL_GENERIC_NO_MEM);
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto error;
    }

  cond_sql = (char *) malloc (max_cond_sql_size);
  if (sql == NULL)
    {
      util_log_write_errid (MSGCAT_UTIL_GENERIC_NO_MEM);
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto error;
    }

  start_ptr = ptr = *loginfo;

  classop = sm_find_class (classname);
  error = au_fetch_class (classop, &class_, AU_FETCH_READ, AU_SELECT);
  if (error != NO_ERROR)
    {
      goto error;
    }

  sprintf (sql, "update [%s] set ", classname);
  strcpy (cond_sql, " where ");

  ptr = or_unpack_int (ptr, &num_change_col);
  for (i = 0; i < num_change_col; i++)
    {
      ptr = or_unpack_int (ptr, &change_index);
    }

  attr = class_->ordered_attributes;
  for (i = 0; i < num_change_col; i++)
    {
      /* check SQL length
       * cond_sql + column name + length of " = and/limit 1;" */
      error =
    flashback_check_and_resize_sql_memory (&cond_sql, strlen (cond_sql) + strlen (attr->header.name) + 15,
                           &max_cond_sql_size);
      if (error != NO_ERROR)
    {
      goto error;
    }

      sprintf (cond_sql + strlen (cond_sql), "[%s] = ", attr->header.name);

      flashback_process_column_data (&ptr, &cond_sql, &max_cond_sql_size, attr->type->id, true);
      if (i != num_change_col - 1)
    {
      strcat (cond_sql, " and ");
    }
      else
    {
      strcat (cond_sql, " limit 1;");
    }

      attr = attr->order_link;
    }

  ptr = or_unpack_int (ptr, &num_cond_col);
  for (i = 0; i < num_change_col; i++)
    {
      ptr = or_unpack_int (ptr, &cond_index);
    }

  attr = class_->ordered_attributes;
  for (i = 0; i < num_cond_col; i++)
    {
      /* check SQL length
       * sql + column name + length of " = , " */
      error =
    flashback_check_and_resize_sql_memory (&sql, strlen (sql) + strlen (attr->header.name) + 8, &max_sql_size);
      if (error != NO_ERROR)
    {
      goto error;
    }

      sprintf (sql + strlen (sql), "[%s] = ", attr->header.name);

      flashback_process_column_data (&ptr, &sql, &max_sql_size, attr->type->id, false);
      if (i != num_cond_col - 1)
    {
      strcat (sql, ", ");
    }

      attr = attr->order_link;
    }

  error = flashback_check_and_resize_sql_memory (&sql, strlen (sql) + strlen (cond_sql) + 1, &max_sql_size);
  if (error != NO_ERROR)
    {
      goto error;
    }

  strcat (sql, cond_sql);

  if (is_detail)
    {
      ptr = *loginfo;

      original_sql = (char *) malloc (max_original_size);
      if (original_sql == NULL)
    {
      util_log_write_errid (MSGCAT_UTIL_GENERIC_NO_MEM);
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto error;
    }

      sprintf (original_sql, "update [%s] set ", classname);

      ptr = or_unpack_int (ptr, &num_change_col);
      for (i = 0; i < num_change_col; i++)
    {
      ptr = or_unpack_int (ptr, &change_index);
    }

      attr = class_->ordered_attributes;
      for (i = 0; i < num_change_col; i++)
    {
      /* check SQL length
       * cond_sql + column name + length of " = , " */
      error =
        flashback_check_and_resize_sql_memory (&original_sql,
                           strlen (original_sql) + strlen (attr->header.name) + 8,
                           &max_original_size);
      if (error != NO_ERROR)
        {
          goto error;
        }

      sprintf (original_sql + strlen (original_sql), "[%s] = ", attr->header.name);

      flashback_process_column_data (&ptr, &original_sql, &max_original_size, attr->type->id, false);
      if (i != num_change_col - 1)
        {
          strcat (original_sql, ", ");
        }

      attr = attr->order_link;
    }

      /* check SQL length
       * original_sql + length of " where " */
      error = flashback_check_and_resize_sql_memory (&original_sql, strlen (original_sql) + 7, &max_original_size);
      if (error != NO_ERROR)
    {
      goto error;
    }

      strcat (original_sql, " where ");

      ptr = or_unpack_int (ptr, &num_cond_col);
      for (i = 0; i < num_cond_col; i++)
    {
      ptr = or_unpack_int (ptr, &cond_index);
    }

      attr = class_->ordered_attributes;
      for (i = 0; i < num_cond_col; i++)
    {
      /* check SQL length
       * cond_sql + column name + length of " = and/limit 1;" */
      error =
        flashback_check_and_resize_sql_memory (&original_sql,
                           strlen (original_sql) + strlen (attr->header.name) + 15,
                           &max_original_size);
      if (error != NO_ERROR)
        {
          goto error;
        }

      sprintf (original_sql + strlen (original_sql), "[%s] = ", attr->header.name);

      flashback_process_column_data (&ptr, &original_sql, &max_original_size, attr->type->id, true);
      if (i != num_cond_col - 1)
        {
          strcat (original_sql, " and ");
        }
      else
        {
          strcat (original_sql, " limit 1;");
        }

      attr = attr->order_link;
    }
    }

  *loginfo = ptr;

  if (is_detail)
    {
      flashback_print_detail (trid, user, sql, original_sql, outfp);
    }
  else
    {
      fprintf (outfp, "%s\n", sql);
    }

  free_and_init (sql);
  free_and_init (cond_sql);

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

  return NO_ERROR;

error:
  if (sql != NULL)
    {
      free_and_init (sql);
    }

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

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

  return error;
}

static int
flashback_print_delete (char **loginfo, int trid, char *user, const char *classname, bool is_detail, FILE * outfp)
{
  MOP classop;
  SM_CLASS *class_;
  SM_ATTRIBUTE *attr;

  int error = NO_ERROR;

  char *sql = NULL;
  int max_sql_size = 1024;

  char *original_sql = NULL;
  int max_original_size = 1024;

  char *ptr;

  int num_change_col;

  int num_cond_col;
  int cond_index;

  int i = 0;

  sql = (char *) malloc (max_sql_size);
  if (sql == NULL)
    {
      util_log_write_errid (MSGCAT_UTIL_GENERIC_NO_MEM);
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto error;
    }

  ptr = *loginfo;

  classop = sm_find_class (classname);
  error = au_fetch_class (classop, &class_, AU_FETCH_READ, AU_SELECT);
  if (error != NO_ERROR)
    {
      goto error;
    }

  sprintf (sql, "insert into [%s] values (", classname);

  ptr = or_unpack_int (ptr, &num_change_col);
  ptr = or_unpack_int (ptr, &num_cond_col);

  for (i = 0; i < num_cond_col; i++)
    {
      ptr = or_unpack_int (ptr, &cond_index);
    }

  attr = class_->ordered_attributes;
  for (i = 0; i < num_cond_col; i++)
    {
      /* check SQL length
       * sql + length of ", " */
      error = flashback_check_and_resize_sql_memory (&sql, strlen (sql) + 3, &max_sql_size);
      if (error != NO_ERROR)
    {
      goto error;
    }

      flashback_process_column_data (&ptr, &sql, &max_sql_size, attr->type->id, false);
      if (i != num_cond_col - 1)
    {
      strcat (sql, ", ");
    }
      else
    {
      strcat (sql, ");");
    }

      attr = attr->order_link;
    }

  if (is_detail)
    {
      ptr = *loginfo;

      original_sql = (char *) malloc (max_original_size);
      if (original_sql == NULL)
    {
      util_log_write_errid (MSGCAT_UTIL_GENERIC_NO_MEM);
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto error;
    }
      sprintf (original_sql, "delete from [%s] where ", classname);

      ptr = or_unpack_int (ptr, &num_change_col);
      ptr = or_unpack_int (ptr, &num_cond_col);

      for (i = 0; i < num_cond_col; i++)
    {
      ptr = or_unpack_int (ptr, &cond_index);
    }

      attr = class_->ordered_attributes;
      for (i = 0; i < num_cond_col; i++)
    {
      /* check SQL length
       * cond_sql + column name + length of " = and/limit 1" */
      error =
        flashback_check_and_resize_sql_memory (&sql, strlen (original_sql) + strlen (attr->header.name) + 15,
                           &max_sql_size);
      if (error != NO_ERROR)
        {
          goto error;
        }

      sprintf (original_sql + strlen (original_sql), "[%s] = ", attr->header.name);

      flashback_process_column_data (&ptr, &original_sql, &max_original_size, attr->type->id, true);

      if (i != num_cond_col - 1)
        {
          strcat (original_sql, " and ");
        }
      else
        {
          strcat (original_sql, " limit 1;");
        }

      attr = attr->order_link;
    }
    }

  *loginfo = ptr;

  if (is_detail)
    {
      flashback_print_detail (trid, user, sql, original_sql, outfp);
    }
  else
    {
      fprintf (outfp, "%s\n", sql);
    }

  free_and_init (sql);
  if (original_sql != NULL)
    {
      free_and_init (original_sql);
    }

  return NO_ERROR;

error:
  if (sql != NULL)
    {
      free_and_init (sql);
    }

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

  return error;
}

static int
flashback_print_insert (char **loginfo, int trid, char *user, const char *classname, bool is_detail, FILE * outfp)
{
  MOP classop;
  SM_CLASS *class_;
  SM_ATTRIBUTE *attr;

  int error = NO_ERROR;

  char *sql = NULL;
  int max_sql_size = 1024;

  char *original_sql = NULL;
  int max_original_size = 1024;

  char *ptr;

  int num_change_col;
  int change_index;

  int num_cond_col;

  int i = 0;

  sql = (char *) malloc (max_sql_size);
  if (sql == NULL)
    {
      util_log_write_errid (MSGCAT_UTIL_GENERIC_NO_MEM);
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto error;
    }

  ptr = *loginfo;

  classop = sm_find_class (classname);
  error = au_fetch_class (classop, &class_, AU_FETCH_READ, AU_SELECT);
  if (error != NO_ERROR)
    {
      goto error;
    }

  sprintf (sql, "delete from [%s] where ", classname);

  ptr = or_unpack_int (ptr, &num_change_col);
  for (i = 0; i < num_change_col; i++)
    {
      ptr = or_unpack_int (ptr, &change_index);
    }

  attr = class_->ordered_attributes;
  for (i = 0; i < num_change_col; i++)
    {
      /* check SQL length
       * cond_sql + column name + length of " = and/limit 1" */
      error =
    flashback_check_and_resize_sql_memory (&sql, strlen (sql) + strlen (attr->header.name) + 15, &max_sql_size);
      if (error != NO_ERROR)
    {
      goto error;
    }

      sprintf (sql + strlen (sql), "[%s] = ", attr->header.name);

      flashback_process_column_data (&ptr, &sql, &max_sql_size, attr->type->id, true);
      if (i != num_change_col - 1)
    {
      strcat (sql, " and ");
    }
      else
    {
      strcat (sql, " limit 1;");
    }

      attr = attr->order_link;
    }

  if (is_detail)
    {
      ptr = *loginfo;

      original_sql = (char *) malloc (max_original_size);
      if (original_sql == NULL)
    {
      util_log_write_errid (MSGCAT_UTIL_GENERIC_NO_MEM);
      error = ER_OUT_OF_VIRTUAL_MEMORY;
      goto error;
    }

      sprintf (original_sql, "insert into [%s] values (", classname);

      ptr = or_unpack_int (ptr, &num_change_col);
      for (i = 0; i < num_change_col; i++)
    {
      ptr = or_unpack_int (ptr, &change_index);
    }

      attr = class_->ordered_attributes;
      for (i = 0; i < num_change_col; i++)
    {
      /* check SQL length
       * cond_sql + length of ", " */
      error = flashback_check_and_resize_sql_memory (&original_sql, strlen (original_sql) + 3, &max_original_size);
      if (error != NO_ERROR)
        {
          goto error;
        }

      flashback_process_column_data (&ptr, &original_sql, &max_original_size, attr->type->id, false);
      if (i != num_change_col - 1)
        {
          strcat (original_sql, ", ");
        }
      else
        {
          strcat (original_sql, ");");
        }

      attr = attr->order_link;
    }
    }

  ptr = or_unpack_int (ptr, &num_cond_col);

  *loginfo = ptr;

  if (is_detail)
    {
      flashback_print_detail (trid, user, sql, original_sql, outfp);
    }
  else
    {
      fprintf (outfp, "%s\n", sql);
    }

  free_and_init (sql);
  if (original_sql != NULL)
    {
      free_and_init (original_sql);
    }

  return NO_ERROR;

error:
  if (sql != NULL)
    {
      free_and_init (sql);
    }

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

  return error;
}

int
flashback_print_loginfo (char *loginfo, int num_item, dynamic_array * classlist, OID * oidlist, bool is_detail,
             FILE * outfp)
{
  int length;
  TRANID trid;
  char *user;
  int dataitem_type;

  int error = NO_ERROR;

  FLASHBACK_DML_TYPE dml_type;

  INT64 b_classoid;
  OID classoid;
  int class_index = 0;
  char classname[SM_MAX_IDENTIFIER_LENGTH] = "\0";

  char *ptr = loginfo;

  for (int i = 0; i < num_item; i++)
    {
      ptr = PTR_ALIGN (ptr, MAX_ALIGNMENT);
      ptr = or_unpack_int (ptr, &length);
      ptr = or_unpack_int (ptr, &trid);
      ptr = or_unpack_string_nocopy (ptr, &user);
      ptr = or_unpack_int (ptr, &dataitem_type);

      ptr = or_unpack_int (ptr, (int *) &dml_type);
      ptr = or_unpack_int64 (ptr, &b_classoid);
      memcpy (&classoid, &b_classoid, sizeof (OID));

      class_index = flashback_find_class_index (oidlist, da_size (classlist), classoid);

      da_get (classlist, class_index, classname);

      switch (dml_type)
    {
    case FLASHBACK_INSERT:
      error = flashback_print_insert (&ptr, trid, user, classname, is_detail, outfp);
      break;
    case FLASHBACK_UPDATE:
      /* UPDATE */
      error = flashback_print_update (&ptr, trid, user, classname, is_detail, outfp);
      break;
    case FLASHBACK_DELETE:
      /* DELETE */
      error = flashback_print_delete (&ptr, trid, user, classname, is_detail, outfp);
      break;
    default:
      assert (false);
      break;
    }

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

  return NO_ERROR;
}