Skip to content

File broker_log_top.c

File List > broker > broker_log_top.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.
 *
 */


/*
 * broker_log_top.c -
 */

#ident "$Id$"

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#if !defined(WINDOWS)
#include <unistd.h>
#endif

#ifdef MT_MODE
#include <pthread.h>
#endif

#include "cubrid_getopt.h"
#include "cas_common.h"
#include "cas_query_info.h"
#include "broker_log_time.h"
#include "broker_log_sql_list.h"
#include "log_top_string.h"
#include "broker_log_top.h"
#include "broker_log_util.h"

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

#define MAX_SRV_HANDLE      3000
#define CLIENT_MSG_BUF_SIZE 1024
#define CONNECT_MSG_BUF_SIZE    1024

#ifdef MT_MODE
typedef struct t_work_msg T_WORK_MSG;
struct t_work_msg
{
  FILE *fp;
  char *filename;
};
#endif

static int log_top_query (int argc, char *argv[], int arg_start);
static int log_top (FILE * fp, char *filename, long start_offset, long end_offset);
static int log_execute (T_QUERY_INFO * qi, char *linebuf, char **query_p);
static int get_args (int argc, char *argv[]);
#if defined(WINDOWS)
static int get_file_count (int argc, char *argv[], int arg_start);
static int get_file_list (char *list[], int size, int argc, char *argv[], int arg_start);
static char **alloc_file_list (int size);
static void free_file_list (char **list, int size);
#endif
#ifdef MT_MODE
static void *thr_main (void *arg);
#endif
static int read_multi_line_sql (FILE * fp, T_STRING * t_str, char **linebuf, int *lineno, T_STRING * sql_buf,
                T_STRING * cas_log_buf);
static int read_execute_end_msg (char *msg_p, int *res_code, int *runtime_msec);
static int read_bind_value (FILE * fp, T_STRING * t_str, char **linebuf, int *lineno, T_STRING * cas_log_buf);
static int search_offset (FILE * fp, char *string, long *offset, bool start);
static char *organize_query_string (const char *sql);

T_LOG_TOP_MODE log_top_mode = MODE_PROC_TIME;

static char *sql_info_file = NULL;
static int mode_max_handle_lower_bound;
static char mode_tran = 0;
static char from_date[128] = "";
static char to_date[128] = "";

#ifdef MT_MODE
static int num_thread = 5;
static int process_flag = 1;
static T_WORK_MSG *work_msg;
#endif
int
main (int argc, char *argv[])
{
  int arg_start;
  int error = 0;
#if defined(WINDOWS)
  int file_cnt = -1;
  int get_cnt = 0;
  char **file_list = NULL;
#endif


  arg_start = get_args (argc, argv);
  if (arg_start < 0)
    {
      return -1;
    }

#if defined(WINDOWS)
  file_cnt = get_file_count (argc, argv, arg_start);
  if (file_cnt <= 0)
    {
      return -1;
    }

  file_list = alloc_file_list (file_cnt);
  if (file_list == NULL)
    {
      return -1;
    }

  get_cnt = get_file_list (file_list, file_cnt, argc, argv, arg_start);
  if (get_cnt > file_cnt)
    {
      get_cnt = file_cnt;
    }

  if (mode_tran)
    {
      error = log_top_tran (get_cnt, file_list, 0);
    }
  else
    {
      error = log_top_query (get_cnt, file_list, 0);
    }

  free_file_list (file_list, file_cnt);
#else
  if (mode_tran)
    {
      error = log_top_tran (argc, argv, arg_start);
    }
  else
    {
      error = log_top_query (argc, argv, arg_start);
    }
#endif

  return error;
}

#if defined(WINDOWS)
int
get_file_count (int argc, char *argv[], int arg_start)
{
  int i;
  int count = 0;
  HANDLE handle;
  WIN32_FIND_DATA find_data;

  for (i = arg_start; i < argc; i++)
    {
      handle = FindFirstFile (argv[i], &find_data);
      if (handle == INVALID_HANDLE_VALUE)
    {
      fprintf (stderr, "No such file or directory[%s]\n", argv[i]);
      return -1;
    }
      do
    {
      /* skip directory */
      if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
        {
          count++;
        }
    }
      while (FindNextFile (handle, &find_data));

      FindClose (handle);
    }

  return count;
}

int
get_file_list (char *list[], int size, int argc, char *argv[], int arg_start)
{
  int i;
  int index = 0;
  HANDLE handle;
  WIN32_FIND_DATA find_data;
  char *slash_pos, *pos1, *pos2;
  char prefix[MAX_PATH] = { 0 };

  assert (list != NULL);

  for (i = arg_start; i < argc; i++)
    {
      handle = FindFirstFile (argv[i], &find_data);
      if (handle == INVALID_HANDLE_VALUE)
    {
      continue;
    }

      /* find the prefix of the matched file */
      pos1 = strrchr (argv[i], '\\');
      pos2 = strrchr (argv[i], '/');
      slash_pos = MAX (pos1, pos2);
      if (slash_pos != NULL)
    {
      strncpy (prefix, argv[i], MAX_PATH);
      prefix[slash_pos - argv[i] + 1] = '\0';
    }

      do
    {
      /* skip directory */
      if (index < size && !(find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
        {
          assert (list[index] != NULL);
          if (slash_pos != NULL)
        {
          snprintf (list[index], MAX_PATH, "%s%s", prefix, find_data.cFileName);
        }
          else
        {
          strncpy (list[index], find_data.cFileName, MAX_PATH);
        }
          index++;
        }
    }
      while (FindNextFile (handle, &find_data));

      FindClose (handle);
    }

  return index;
}

char **
alloc_file_list (int size)
{
  int i, j;
  char **file_list = NULL;

  assert (size > 0);

  file_list = (char **) MALLOC (sizeof (char *) * size);
  if (file_list == NULL)
    {
      fprintf (stderr, "fail memory allocation\n");
      return NULL;
    }

  for (i = 0; i < size; i++)
    {
      file_list[i] = (char *) MALLOC (MAX_PATH);

      if (file_list[i] == NULL)
    {
      fprintf (stderr, "fail memory allocation\n");
      for (j = 0; j < i; j++)
        {
          FREE_MEM (file_list[j]);
        }
      FREE_MEM (file_list);
      return NULL;
    }
    }

  return file_list;
}

void
free_file_list (char **list, int size)
{
  int i;
  assert (list != NULL);

  for (i = 0; i < size; i++)
    {
      if (list[i] == NULL)
    {
      break;
    }
      FREE_MEM (list[i]);
    }
  FREE_MEM (list);
}
#endif

int
get_file_offset (char *filename, long *start_offset, long *end_offset)
{
  FILE *fp;

  if (!start_offset || !end_offset)
    {
      return -1;
    }

  fp = fopen (filename, "r");
  if (fp == NULL)
    {
      return -1;
    }

  if (from_date[0] == '\0' || search_offset (fp, from_date, start_offset, true) < 0)
    {
      *start_offset = -1;
    }

  if (to_date[0] == '\0' || search_offset (fp, to_date, end_offset, false) < 0)
    {
      *end_offset = -1;
    }

  fclose (fp);
  return 0;
}

int
check_log_time (char *start_date, char *end_date)
{
  if (from_date[0])
    {
      if (strncmp (end_date, from_date, DATE_STR_LEN) < 0)
    return -1;
    }
  if (to_date[0])
    {
      if (strncmp (to_date, start_date, DATE_STR_LEN) < 0)
    return -1;
    }

  return 0;
}

static int
log_top_query (int argc, char *argv[], int arg_start)
{
  FILE *fp;
  char *filename;
  int i;
  int error = 0;
  long start_offset, end_offset;
#ifdef MT_MODE
  T_THREAD thrid;
  int j;
#endif

#ifdef MT_MODE
  query_info_mutex_init ();
#endif

#ifdef MT_MODE
  work_msg = MALLOC (sizeof (T_WORK_MSG) * num_thread);
  if (work_msg == NULL)
    {
      fprintf (stderr, "malloc error\n");
      return -1;
    }
  memset (work_msg, 0, sizeof (T_WORK_MSG *) * num_thread);

  for (i = 0; i < num_thread; i++)
    THREAD_BEGIN (thrid, thr_main, (void *) i);
#endif

  for (i = arg_start; i < argc; i++)
    {
      filename = argv[i];
      fprintf (stdout, "%s\n", filename);

#if defined(WINDOWS)
      fp = fopen (filename, "rb");
#else
      fp = fopen (filename, "r");
#endif
      if (fp == NULL)
    {
      fprintf (stderr, "%s[%s]\n", strerror (errno), filename);
#ifdef MT_MODE
      process_flag = 0;
#endif
      return -1;
    }

      if (get_file_offset (filename, &start_offset, &end_offset) < 0)
    {
      start_offset = end_offset = -1;
    }

#ifdef MT_MODE
      while (1)
    {
      for (j = 0; j < num_thread; j++)
        {
          if (work_msg[j].filename == NULL)
        {
          work_msg[j].fp = fp;
          work_msg[j].filename = filename;
          break;
        }
        }
      if (j == num_thread)
        SLEEP_MILISEC (1, 0);
      else
        break;
    }
#else
      error = log_top (fp, filename, start_offset, end_offset);
      fclose (fp);
      if (error == LT_INVAILD_VERSION)
    {
      return error;
    }
#endif
    }

#ifdef MT_MODE
  process_flag = 0;
#endif

  if (sql_info_file != NULL)
    {
      fprintf (stdout, "read sql info file...\n");
      if (sql_list_make (sql_info_file) < 0)
    {
      return -1;
    }
    }

  fprintf (stdout, "print results...\n");
  query_info_print ();

  return 0;
}

#ifdef MT_MODE
static void *
thr_main (void *arg)
{
  int self_index = (int) arg;

  while (process_flag)
    {
      if (work_msg[self_index].filename == NULL)
    {
      SLEEP_MILISEC (0, 100);
    }
      else
    {
      log_top (work_msg[self_index].fp, work_msg[self_index].filename);
      fclose (work_msg[self_index].fp);
      work_msg[self_index].fp = NULL;
      work_msg[self_index].filename = NULL;
    }
    }
  return NULL;
}
#endif

static int
log_top (FILE * fp, char *filename, long start_offset, long end_offset)
{
  char *linebuf = NULL;
  T_QUERY_INFO query_info_buf[MAX_SRV_HANDLE];
  char client_msg_buf[CLIENT_MSG_BUF_SIZE];
  char connect_msg_buf[CONNECT_MSG_BUF_SIZE];
  T_STRING *cas_log_buf = NULL;
  T_STRING *sql_buf = NULL;
  T_STRING *linebuf_tstr = NULL;
  char prepare_buf[128];
  int i;
  char *msg_p;
  int lineno = 0;
  int log_type = 0;
  char read_flag = 1;
  char cur_date[DATE_STR_LEN + 1];
  char start_date[DATE_STR_LEN + 1];
  start_date[0] = '\0';

  for (i = 0; i < MAX_SRV_HANDLE; i++)
    {
      query_info_init (&query_info_buf[i]);
    }

  cas_log_buf = t_string_make (1);

  sql_buf = t_string_make (1);
  linebuf_tstr = t_string_make (1000);
  if (cas_log_buf == NULL || sql_buf == NULL || linebuf_tstr == NULL)
    {
      fprintf (stderr, "malloc error\n");
      goto log_top_err;
    }

  memset (client_msg_buf, 0, sizeof (client_msg_buf));
  memset (connect_msg_buf, 0, sizeof (connect_msg_buf));
  t_string_clear (cas_log_buf);
  t_string_clear (sql_buf);
  memset (prepare_buf, 0, sizeof (prepare_buf));

  if (start_offset != -1)
    {
      fseek (fp, start_offset, SEEK_SET);
    }

  while (1)
    {
      if (end_offset != -1)
    {
      if (ftell (fp) > end_offset)
        {
          break;
        }
    }

      if (read_flag)
    {
      if (ut_get_line (fp, linebuf_tstr, &linebuf, &lineno) <= 0)
        {
          break;
        }
    }
      read_flag = 1;

      log_type = is_cas_log (linebuf);
      if (log_type == CAS_LOG_BEGIN_WITH_MONTH)
    {
      fprintf (stderr, "invaild version of log file\n");
      t_string_free (cas_log_buf);
      t_string_free (sql_buf);
      t_string_free (linebuf_tstr);
      return LT_INVAILD_VERSION;
    }
      else if (log_type != CAS_LOG_BEGIN_WITH_YEAR)
    {
      continue;
    }

      if (strncmp (linebuf + 23, "END OF LOG", 10) == 0)
    {
      break;
    }

      GET_CUR_DATE_STR (cur_date, linebuf);
      if (start_date[0] == '\0')
    {
      strcpy (start_date, cur_date);
    }

      msg_p = get_msg_start_ptr (linebuf);
      if (strncmp (msg_p, "execute", 7) == 0 || strncmp (msg_p, "execute_all", 11) == 0
      || strncmp (msg_p, "execute_call", 12) == 0 || strncmp (msg_p, "execute_batch", 13) == 0)
    {
      int qi_idx;
      char *query_p;
      int end_block_flag = 0;

      /*
       * execute log format:
       * <execute_cmd> srv_h_id <handle_id> <query_string>
       * bind <bind_index> : <TYPE> <VALUE>
       * <execute_cmd> [error:]<res> tuple <tuple_count> time <runtime_msec>
       * <execute_cmd>:
       *      execute, execute_all or execute_call
       *
       * ex)
       * execute srv_h_id 1 select 'a' from db_root
       * bind 1 : VARCHAR test str
       * execute 0 tuple 1 time 0.004
       */
      qi_idx = log_execute (query_info_buf, linebuf, &query_p);
      if (qi_idx < 0 || query_p == NULL)
        goto log_top_err;

      t_string_clear (sql_buf);
      t_string_clear (cas_log_buf);

      t_string_add (sql_buf, query_p, strlen (query_p));
      t_string_add (cas_log_buf, linebuf, strlen (linebuf));

      if (read_multi_line_sql (fp, linebuf_tstr, &linebuf, &lineno, sql_buf, cas_log_buf) < 0)
        {
          break;
        }
      if (read_bind_value (fp, linebuf_tstr, &linebuf, &lineno, cas_log_buf) < 0)
        {
          break;
        }

      msg_p = get_msg_start_ptr (linebuf);

      /* skip query_cancel */
      if (strncmp (msg_p, "query_cancel", 12) == 0)
        {
          if (ut_get_line (fp, linebuf_tstr, &linebuf, &lineno) <= 0)
        {
          break;
        }
        }

      if (strncmp (msg_p, "execute", 7) != 0)
        {
          while (1)
        {
          if (ut_get_line (fp, linebuf_tstr, &linebuf, &lineno) <= 0)
            {
              break;
            }

          msg_p = get_msg_start_ptr (linebuf);
          if (strncmp (msg_p, "***", 3) == 0)
            {
              end_block_flag = 1;
              if (ut_get_line (fp, linebuf_tstr, &linebuf, &lineno) <= 0)
            {
              /* ut_get_line error, just break; */
              break;
            }
              break;
            }
        }
        }

      if (end_block_flag == 1)
        {
          continue;
        }

      query_info_buf[qi_idx].sql = (char *) REALLOC (query_info_buf[qi_idx].sql, t_string_len (sql_buf) + 1);

      strcpy (query_info_buf[qi_idx].sql, ut_trim (t_string_str (sql_buf)));
      query_info_buf[qi_idx].organized_sql = organize_query_string (query_info_buf[qi_idx].sql);

      msg_p = get_msg_start_ptr (linebuf);
      GET_CUR_DATE_STR (cur_date, linebuf);

      strcpy (query_info_buf[qi_idx].start_date, start_date);

      if (log_top_mode == MODE_MAX_HANDLE)
        {
          if (qi_idx >= mode_max_handle_lower_bound)
        {
          if (query_info_add (&query_info_buf[qi_idx], qi_idx + 1, 0, filename, lineno, cur_date) < 0)
            {
              goto log_top_err;
            }
        }
        }
      else
        {
          int execute_res, runtime;

          /* set cas_log to query info */
          if (t_string_add (cas_log_buf, linebuf, strlen (linebuf)) < 0)
        {
          goto log_top_err;
        }

          query_info_buf[qi_idx].cas_log =
        (char *) REALLOC (query_info_buf[qi_idx].cas_log, t_string_len (cas_log_buf) + 1);

          memcpy (query_info_buf[qi_idx].cas_log, t_string_str (cas_log_buf), t_string_len (cas_log_buf));

          query_info_buf[qi_idx].cas_log_len = t_string_len (cas_log_buf);


          /* read execute info & if fail add to query_info_arr_ne */
          if (read_execute_end_msg (msg_p, &execute_res, &runtime) < 0)
        {
          if (query_info_add_ne (&query_info_buf[qi_idx], cur_date) < 0)
            {
              goto log_top_err;
            }

          read_flag = 0;
          continue;
        }

          /* add to query_info_arr */
          if (query_info_add (&query_info_buf[qi_idx], runtime, execute_res, filename, lineno, cur_date) < 0)
        {
          goto log_top_err;
        }
        }
    }
      start_date[0] = '\0';
    }

  for (i = 0; i < MAX_SRV_HANDLE; i++)
    {
      query_info_clear (&query_info_buf[i]);
    }

  t_string_free (cas_log_buf);
  t_string_free (sql_buf);
  t_string_free (linebuf_tstr);
  return LT_NO_ERROR;

log_top_err:
  t_string_free (cas_log_buf);
  t_string_free (sql_buf);
  t_string_free (linebuf_tstr);
  return LT_OTHER_ERROR;
}

static int
log_execute (T_QUERY_INFO * qi, char *linebuf, char **query_p)
{
  char *p;
  int exec_h_id;

  p = strstr (linebuf, "srv_h_id ");
  if (p == NULL)
    {
      fprintf (stderr, "log error[%s]\n", linebuf);
      return -1;
    }
  exec_h_id = atoi (p + 9);
  *query_p = strchr (p + 9, ' ');
  if (*query_p)
    *query_p = *query_p + 1;

  if (exec_h_id <= 0 || exec_h_id > MAX_SRV_HANDLE)
    {
      fprintf (stderr, "log error. exec id = %d\n", exec_h_id);
      return -1;
    }
  exec_h_id--;

  return exec_h_id;
}

static int
get_args (int argc, char *argv[])
{
  int c;

  while ((c = getopt (argc, argv, "tq:h:F:T:")) != EOF)
    {
      switch (c)
    {
    case 't':
      mode_tran = 1;
      break;
    case 'q':
      sql_info_file = optarg;
      break;
    case 'h':
      mode_max_handle_lower_bound = atoi (optarg);
      break;
    case 'F':
      if (str_to_log_date_format (optarg, from_date) < 0)
        {
          goto date_format_err;
        }
      break;
    case 'T':
      if (str_to_log_date_format (optarg, to_date) < 0)
        {
          goto date_format_err;
        }
      break;
    default:
      goto getargs_err;
    }
    }

  if (mode_max_handle_lower_bound > 0)
    log_top_mode = MODE_MAX_HANDLE;

  if (optind < argc)
    return optind;

getargs_err:
  fprintf (stderr, "%s [-t] [-F <from date>] [-T <to date>] <log_file> ...\n", argv[0]);
  return -1;
date_format_err:
  fprintf (stderr, "invalid date. valid date format is yy-mm-dd hh:mm:ss.\n");
  return -1;
}

static int
read_multi_line_sql (FILE * fp, T_STRING * t_str, char **linebuf, int *lineno, T_STRING * sql_buf,
             T_STRING * cas_log_buf)
{
  while (1)
    {
      if (ut_get_line (fp, t_str, linebuf, lineno) <= 0)
    {
      return -1;
    }

      if (is_cas_log (*linebuf) == CAS_LOG_BEGIN_WITH_YEAR)
    {
      return 0;
    }

      if (t_string_add (sql_buf, *linebuf, strlen (*linebuf)) < 0)
    {
      fprintf (stderr, "malloc error\n");
      return -1;
    }
      if (t_string_add (cas_log_buf, *linebuf, strlen (*linebuf)) < 0)
    {
      fprintf (stderr, "malloc error\n");
      return -1;
    }
    }
}

static int
read_bind_value (FILE * fp, T_STRING * t_str, char **linebuf, int *lineno, T_STRING * cas_log_buf)
{
  char *msg_p;
  char is_bind_value;
  int linebuf_len;

  do
    {
      is_bind_value = 0;

      if (is_cas_log (*linebuf) == CAS_LOG_BEGIN_WITH_YEAR)
    {
      msg_p = get_msg_start_ptr (*linebuf);
      if (strncmp (msg_p, "bind ", 5) == 0)
        is_bind_value = 1;
    }
      else
    {
      is_bind_value = 1;
    }
      if (is_bind_value)
    {
      linebuf_len = t_string_len (t_str);
      if (t_string_add (cas_log_buf, *linebuf, linebuf_len) < 0)
        {
          return -1;
        }
    }
      else
    {
      return 0;
    }

      if (ut_get_line (fp, t_str, linebuf, lineno) <= 0)
    {
      return -1;
    }
    }
  while (1);
}

static int
read_execute_end_msg (char *msg_p, int *res_code, int *runtime_msec)
{
  char *p, *next_p;
  int sec, msec;
  int tuple_count;
  int result = 0;
  int val;

  p = strchr (msg_p, ' ');
  if (p == NULL)
    {
      return -1;
    }
  p++;
  if (strncmp (p, "error:", 6) == 0)
    {
      p += 6;
    }

  result = str_to_int32 (&val, &next_p, p, 10);
  if (result != 0)
    {
      return -1;
    }
  *res_code = val;

  p = next_p + 1;
  if (strncmp (p, "tuple ", 6) != 0)
    {
      return -1;
    }

  p += 6;

  result = str_to_int32 (&val, &next_p, p, 10);
  if (result != 0)
    {
      return -1;
    }
  tuple_count = val;

  p = next_p + 1;
  if (strncmp (p, "time ", 5) != 0)
    {
      return -1;
    }
  p += 5;

  sscanf (p, "%d.%d", &sec, &msec);
  *runtime_msec = sec * 1000 + msec;

  return 0;
}

static int
search_offset (FILE * fp, char *string, long *offset, bool start)
{
  off_t start_ptr = 0;
  off_t end_ptr = 0;
  off_t cur_ptr;
  off_t old_start_ptr = 0;
  bool old_start_saved = false;
  long tmp_offset = -1;
  struct stat stat_buf;
  char *linebuf = NULL;
  int line_no = 0;
  T_STRING *linebuf_tstr = NULL;
  int ret_val;

  assert (offset != NULL);

  *offset = -1;

  if (fstat (fileno (fp), &stat_buf) < 0)
    {
      return -1;
    }

  end_ptr = stat_buf.st_size;

  linebuf_tstr = t_string_make (1000);
  if (linebuf_tstr == NULL)
    {
      return -1;
    }

  cur_ptr = 0;

  while (true)
    {
      if (fseek (fp, cur_ptr, SEEK_SET) < 0)
    {
      goto error;
    }

      while (ut_get_line (fp, linebuf_tstr, &linebuf, &line_no) > 0)
    {
      if (is_cas_log (linebuf) == CAS_LOG_BEGIN_WITH_YEAR)
        {
          break;
        }
      cur_ptr = ftell (fp);

      if (cur_ptr >= end_ptr)
        {
          tmp_offset = old_start_saved ? old_start_ptr : start_ptr;
          goto end_loop;
        }
    }

      ret_val = strncmp (linebuf, string, DATE_STR_LEN);

      if (ret_val < 0)
    {
      old_start_saved = true;
      old_start_ptr = start_ptr;
      start_ptr = ftell (fp);
    }

      if (ret_val >= 0)
    {
      if (ret_val == 0 && old_start_saved)
        {
          tmp_offset = start_ptr;
          goto end_loop;
        }
      else
        {
          old_start_saved = false;
          end_ptr = cur_ptr;
        }
    }

      cur_ptr = start_ptr + (end_ptr - start_ptr) / 2;
      if (cur_ptr <= start_ptr)
    {
      tmp_offset = start_ptr;
      goto end_loop;
    }
    }

end_loop:
  if (fseek (fp, tmp_offset, SEEK_SET) < 0)
    {
      goto error;
    }

  while (ut_get_line (fp, linebuf_tstr, &linebuf, &line_no) > 0)
    {
      if (start)
    {
      /* the first line of the time */
      if (strncmp (linebuf, string, DATE_STR_LEN) >= 0)
        {
          break;
        }
    }
      else
    {
      /* the last line of the time */
      if (strncmp (linebuf, string, DATE_STR_LEN) > 0)
        {
          break;
        }
    }
      tmp_offset = ftell (fp);
    }

  *offset = tmp_offset;
  t_string_free (linebuf_tstr);
  return 0;

error:
  t_string_free (linebuf_tstr);
  return -1;
}

static char *
organize_query_string (const char *sql)
{
  typedef enum
  {
    SQL_TOKEN_NONE = 0,
    SQL_TOKEN_DOUBLE_QUOTE,
    SQL_TOKEN_SINGLE_QUOTE,
    SQL_TOKEN_SQL_COMMENT,
    SQL_TOKEN_C_COMMENT,
    SQL_TOKEN_CPP_COMMENT
  } SQL_TOKEN;

  SQL_TOKEN token = SQL_TOKEN_NONE;
  int token_len = 0;
  char *p = NULL;
  const char *q = NULL;
  char *organized_sql = NULL;
  bool need_copy_token = true;

  organized_sql = (char *) malloc (strlen (sql) + 1);
  if (organized_sql == NULL)
    {
      return NULL;
    }

  p = organized_sql;
  q = sql;

  while (*q != '\0')
    {
      need_copy_token = true;
      token_len = 1;

      if (token == SQL_TOKEN_NONE)
    {
      if (*q == '\'' && (q == sql || *(q - 1) != '\\'))
        {
          token = SQL_TOKEN_SINGLE_QUOTE;
        }
      else if (*q == '"' && (q == sql || *(q - 1) != '\\'))
        {
          token = SQL_TOKEN_DOUBLE_QUOTE;
        }
      else if (*q == '-' && *(q + 1) == '-')
        {
          need_copy_token = false;
          token = SQL_TOKEN_SQL_COMMENT;
          token_len = 2;
        }
      else if (*q == '/' && *(q + 1) == '*')
        {
          need_copy_token = false;
          token = SQL_TOKEN_C_COMMENT;
          token_len = 2;
        }
      else if (*q == '/' && *(q + 1) == '/')
        {
          need_copy_token = false;
          token = SQL_TOKEN_CPP_COMMENT;
          token_len = 2;
        }
    }
      else
    {
      need_copy_token = false;

      if (token == SQL_TOKEN_SINGLE_QUOTE)
        {
          need_copy_token = true;

          if (*q == '\'' && *(q - 1) != '\\')
        {
          token = SQL_TOKEN_NONE;
        }
        }
      else if (token == SQL_TOKEN_DOUBLE_QUOTE)
        {
          need_copy_token = true;

          if (*q == '"' && *(q - 1) != '\\')
        {
          token = SQL_TOKEN_NONE;
        }
        }
      else if ((token == SQL_TOKEN_SQL_COMMENT || token == SQL_TOKEN_CPP_COMMENT) && *q == '\n')
        {
          token = SQL_TOKEN_NONE;
        }
      else if (token == SQL_TOKEN_C_COMMENT && *q == '*' && *(q + 1) == '/')
        {
          token = SQL_TOKEN_NONE;
          token_len = 2;
        }
    }

      if (need_copy_token)
    {
      memcpy (p, q, token_len);
      p += token_len;
    }

      q += token_len;
    }

  *p = '\0';

  return organized_sql;
}