Skip to content

File unload_object.c

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

/*
 * unload_object.c: Utility that emits database object definitions in database
 *               object loader format.
 */

#ident "$Id$"

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

#include "config.h"

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <assert.h>

#include <sys/stat.h>
#if defined(WINDOWS)
#include <io.h>
#include <sys/timeb.h>
#include <time.h>
#include <direct.h>
#define SIGALRM 14
#endif /* WINDOWS */
#include <thread>

#include "authenticate.h"
#include "utility.h"
#include "load_object.h"
#include "unload_object_file.h"
#include "log_lsa.hpp"
#include "file_hash.h"
#include "db.h"
#include "memory_hash.h"
#include "memory_alloc.h"
#include "locator_cl.h"
#include "schema_manager.h"
#include "locator.h"
#include "transform_cl.h"
#include "object_accessor.h"
#include "object_primitive.h"
#include "object_representation.h"
#include "set_object.h"

#include "message_catalog.h"
#include "server_interface.h"
#include "porting.h"
#include "unloaddb.h"

#include "system_parameter.h"
#include "schema_system_catalog_constants.h"
#include "execute_schema.h"
#include "network_interface_cl.h"
#include "transaction_cl.h"
#include "dbtype.h"

#include "error_context.hpp"


#define CREAT_OBJECT_FILE_PERM   0644

#define MARK_CLASS_REQUESTED(cl_no) \
  (class_requested[cl_no / 8] |= 1 << cl_no % 8)
#define MARK_CLASS_REFERENCED(cl_no) \
  (class_referenced[cl_no / 8] |= 1 << cl_no % 8)
#define MARK_CLASS_PROCESSED(cl_no) \
  (class_processed[cl_no / 8] |= 1 << cl_no % 8)
#define IS_CLASS_REQUESTED(cl_no) \
  (class_requested[cl_no / 8] & 1 << cl_no % 8)
#define IS_CLASS_REFERENCED(cl_no) \
  (class_referenced[cl_no / 8] & 1 << cl_no % 8)
#define IS_CLASS_PROCESSED(cl_no) \
  (class_processed[cl_no / 8] & 1 << cl_no % 8)

#define GAUGE_INTERVAL  1

static int output_number = 0;

static FH_TABLE *obj_table = NULL;
static FH_TABLE *cl_table = NULL;

static char *class_requested = NULL;
static char *class_referenced = NULL;
static char *class_processed = NULL;

static OID null_oid;

static const char *prohibited_classes[] = {
  CT_ROOT_NAME,
  CT_USER_NAME,
  CT_AUTHORIZATION_NAME,
  CT_PASSWORD_NAME,
  /* catalog classes */
  CT_TRIGGER_NAME,
  CT_SERIAL_NAME,
  CT_HA_APPLY_INFO_NAME,
  CT_CLASS_NAME,
  CT_ATTRIBUTE_NAME,
  CT_DOMAIN_NAME,
  CT_METHOD_NAME,
  CT_METHSIG_NAME,
  CT_METHARG_NAME,
  CT_METHFILE_NAME,
  CT_QUERYSPEC_NAME,
  CT_RESOLUTION_NAME,       /* currently, not implemented */
  CT_INDEX_NAME,
  CT_INDEXKEY_NAME,
  CT_CLASSAUTH_NAME,
  CT_DATATYPE_NAME,
  CT_STORED_PROC_NAME,
  CT_STORED_PROC_ARGS_NAME,
  CT_STORED_PROC_CODE_NAME,
  CT_PARTITION_NAME,
  CT_COLLATION_NAME,
  CT_CHARSET_NAME,
  CT_DUAL_NAME,
  CT_SERVER_NAME,
  CT_SYNONYM_NAME,
  /* catalog vclasses */
  CTV_CLASS_NAME,
  CTV_SUPER_CLASS_NAME,
  CTV_VCLASS_NAME,
  CTV_ATTRIBUTE_NAME,
  CTV_ATTR_SD_NAME,
  CTV_METHOD_NAME,
  CTV_METHARG_NAME,
  CTV_METHARG_SD_NAME,
  CTV_METHFILE_NAME,
  CTV_INDEX_NAME,
  CTV_INDEXKEY_NAME,
  CTV_AUTH_NAME,
  CTV_TRIGGER_NAME,
  CTV_SERIAL_NAME,
  CTV_HA_APPLY_INFO_NAME,
  CTV_STORED_PROC_NAME,
  CTV_STORED_PROC_ARGS_NAME,
  CTV_PARTITION_NAME,
  CTV_COLLATION_NAME,
  CTV_CHARSET_NAME,
  CTV_SERVER_NAME,
  CTV_SYNONYM_NAME,
  NULL
};

// *INDENT-OFF*
static std::atomic <int64_t>  class_objects_atomic = 0;
static std::atomic <int64_t>  total_objects_atomic = 0;
static std::atomic <int64_t>  failed_objects_atomic = 0;
// *INDENT-ON*

static int64_t approximate_class_objects = 0;
static int64_t total_approximate_class_objects = 0;
static char *gauge_class_name;


static volatile bool writer_thread_proc_terminate = false;
static volatile bool extractor_thread_proc_terminate = false;
static int max_fetched_copyarea_list = 1;
#if !defined(WINDOWS)
static S_WAITING_INFO wi_unload_class;
static S_WAITING_INFO wi_w_blk_getQ;
S_WAITING_INFO wi_write_file;
#endif
#define INVALID_THREAD_ID  ((pthread_t) (-1))

#define OBJECT_SUFFIX "_objects"

#define HEADER_FORMAT   "-------------------------------+--------------------------------\n""    %-25s  |  %23s \n""-------------------------------+--------------------------------\n"
#define MSG_FORMAT  "    %-25s  |  %10ld (%3d%% / %5d%%)"

#define ALIGN_SPACE_FMT "    %-25s  | "



#if defined(MULTI_PROCESSING_UNLOADDB_WITH_FORK)
static char *p_unloadlog_filename = NULL;
static void unload_log_write (char *log_file_name, const char *fmt, ...);
#else
static FILE *unloadlog_file = NULL;
#endif

pthread_mutex_t g_update_hash_cs_lock = PTHREAD_MUTEX_INITIALIZER;

typedef struct _unloaddb_class_info UNLD_CLASS_PARAM;
struct _unloaddb_class_info
{
  HFID *hfid;
  DB_OBJECT *class_;
  OID *class_oid;
  SM_CLASS *class_ptr;
  int referenced_class;
  extract_context *pctxt;

#if !defined(WINDOWS)
  S_WAITING_INFO wi_fetch;
#endif

  pthread_mutex_t mtx;
  pthread_cond_t cond;
  volatile bool signaled;
  volatile bool broadcasted;

  class copyarea_list *cparea_lst_ref;
};
UNLD_CLASS_PARAM *g_uci = NULL;

typedef struct _lc_copyarea_node LC_COPYAREA_NODE;
struct _lc_copyarea_node
{
  LC_COPYAREA *lc_copy_area;
  LC_COPYAREA_NODE *next;
};

class copyarea_list
{
private:
  pthread_mutex_t m_cs_lock;
  LC_COPYAREA_NODE *m_root;
  LC_COPYAREA_NODE *m_tail;

  LC_COPYAREA_NODE *m_free;

  int m_max;
  int m_used;

  void clear_freelist ()
  {
    LC_COPYAREA_NODE *pt;
    LC_COPYAREA_NODE *pt_free;

      pthread_mutex_lock (&m_cs_lock);
      pt_free = m_free;
      m_free = NULL;
      pthread_mutex_unlock (&m_cs_lock);

    while (pt_free)
      {
    pt = pt_free;
    pt_free = pt_free->next;

    locator_free_copy_area (pt->lc_copy_area);
    free (pt);
      }
  }

public:
#if !defined(WINDOWS)
    S_WAITING_INFO m_wi_add_list;
#endif

public:
  copyarea_list (int max = 64)
  {
    pthread_mutex_init (&m_cs_lock, NULL);
    m_root = m_tail = NULL;
    m_free = NULL;
    m_max = max;
    m_used = 0;
    TIMER_CLEAR (&m_wi_add_list);
  }
  ~copyarea_list ()
  {
    LC_COPYAREA_NODE *pt;
    while (m_root)
      {
    pt = m_root;
    m_root = m_root->next;
    if (pt->lc_copy_area)
      {
        locator_free_copy_area (pt->lc_copy_area);
      }
    free (pt);
      }
    m_root = m_tail = NULL;

    clear_freelist ();
    (void) pthread_mutex_destroy (&m_cs_lock);
  }

  void set_max_list (int n)
  {
    m_max = n;
  }

  void add_freelist (LC_COPYAREA_NODE * node)
  {
    pthread_mutex_lock (&m_cs_lock);
    if (m_free == NULL)
      node->next = NULL;
    else
      node->next = m_free;

    m_free = node;
    pthread_mutex_unlock (&m_cs_lock);
  }

  void add (LC_COPYAREA * fetch_area, bool try_clear)
  {
    LC_COPYAREA_NODE *pt;
    bool tm_chk_flag = false;

    if (try_clear)
      {
    clear_freelist ();
      }

    pt = (LC_COPYAREA_NODE *) malloc (sizeof (LC_COPYAREA_NODE));
    pt->next = NULL;
    pt->lc_copy_area = fetch_area;

    pthread_mutex_lock (&m_cs_lock);
    while (m_max <= m_used)
      {
    pthread_mutex_unlock (&m_cs_lock);
    if (!tm_chk_flag)
      {
        TIMER_BEGIN ((g_sampling_records >= 0), &m_wi_add_list);
        tm_chk_flag = true;
      }
    YIELD_THREAD ();
    pthread_mutex_lock (&m_cs_lock);
      }

    if (tm_chk_flag)
      {
    TIMER_END ((g_sampling_records >= 0), &m_wi_add_list);
      }

    if (m_root == NULL)
      {
    m_root = pt;
    m_tail = m_root;
      }
    else
      {
    assert (m_tail);
    m_tail->next = pt;
    m_tail = pt;
      }
    m_used++;
    pthread_mutex_unlock (&m_cs_lock);
  }

  LC_COPYAREA_NODE *get ()
  {
    LC_COPYAREA_NODE *pt = NULL;

    pthread_mutex_lock (&m_cs_lock);
    if (m_root != NULL)
      {
    pt = m_root;
    m_root = m_root->next;
    if (m_root == NULL)
      {
        m_tail = NULL;
      }
    m_used--;
      }
    pthread_mutex_unlock (&m_cs_lock);
    return pt;
  }
};

static int get_estimated_objs (HFID * hfid, int64_t * est_objects);
static int set_referenced_subclasses (DB_OBJECT * class_);
static bool check_referenced_domain (DB_DOMAIN * dom_list, bool set_cls_ref, int *num_cls_refp);
static void extractobjects_cleanup ();
static void extractobjects_term_handler (int sig);
static bool mark_referenced_domain (SM_CLASS * class_ptr, int *num_set);
static void gauge_alarm_handler (int sig);
static int process_class (extract_context & ctxt, int cl_no, int nthreads);
static int process_object (DESC_OBJ * desc_obj, OID * obj_oid, int referenced_class, TEXT_OUTPUT * obj_out);
static int process_set (DB_SET * set, TEXT_OUTPUT * obj_out);
static int process_value (DB_VALUE * value, TEXT_OUTPUT * obj_out);
static void update_hash (OID * object_oid, OID * class_oid, int *data);
static DB_OBJECT *is_class (OID * obj_oid, OID * class_oid);
static int all_classes_processed (void);
static int create_filename (const char *output_dirname, const char *output_prefix, const char *suffix,
                char *output_filename_p, const size_t filename_size);

static void close_object_file ();
static bool open_object_file (extract_context & ctxt, const char *output_dirname, const char *class_name);
static bool init_thread_param (const char *output_dirname, int nthreads);
static void quit_thread_param ();
static void print_monitoring_info (const char *class_name, int nthreads);

/*
 * get_estimated_objs - get the estimated number of object reside in file heap
 *    return: NO_ERROR if success, error code otherwise
 *    hfid(in): file heap id
 *    est_objects(out): estimated number of object
 */
static int
get_estimated_objs (HFID * hfid, int64_t * est_objects, bool enhanced)
{
  int ignore_npages;
  int nobjs = 0;
  int error = NO_ERROR;

#define USE_APPROXIMATION      (1)
#define USE_ACTUAL             (0)

  error = heap_get_class_num_objects_pages (hfid, (enhanced ? USE_ACTUAL : USE_APPROXIMATION), &nobjs, &ignore_npages);
  if (error < 0)
    return error;

  *est_objects += nobjs;

  return 0;
}

/*
 * set_referenced_subclasses - set class as referenced
 *    return: NO_ERROR, if successful, error number, if not successful.
 *    class(in): root class
 * Note:
 *    CURRENTLY, ALWAYS RETURN NO_ERROR
 */
static int
set_referenced_subclasses (DB_OBJECT * class_)
{
  int error = NO_ERROR;
  int *cls_no_ptr;
  SM_CLASS *class_ptr;
  DB_OBJLIST *u;
  bool check_reference_chain = false;
  int num_set;
  int error_code;

  error_code = fh_get (cl_table, ws_oid (class_), (FH_DATA *) (&cls_no_ptr));
  if (error_code == NO_ERROR && cls_no_ptr != NULL)
    {
      if (input_filename)
    {
      if (include_references || is_req_class (class_))
        {
          if (!IS_CLASS_REFERENCED (*cls_no_ptr) && !IS_CLASS_REQUESTED (*cls_no_ptr))
        {
          check_reference_chain = true;
        }
          MARK_CLASS_REFERENCED (*cls_no_ptr);
        }
    }
      else
    {
      if (!IS_CLASS_REFERENCED (*cls_no_ptr) && !IS_CLASS_REQUESTED (*cls_no_ptr))
        {
          check_reference_chain = true;
        }
      MARK_CLASS_REFERENCED (*cls_no_ptr);
    }
    }
  else
    {
#if defined(CUBRID_DEBUG) || defined(CUBRID_DEBUG_TEST)
      fprintf (stderr, "cls_no_ptr is NULL\n");
#endif /* CUBRID_DEBUG */
    }

  ws_find (class_, (MOBJ *) (&class_ptr));
  if (class_ptr == NULL)
    {
      goto exit_on_error;
    }

  if (check_reference_chain)
    {
      mark_referenced_domain (class_ptr, &num_set);
    }

  /* dive to the bottom */
  for (u = class_ptr->users; u != NULL && error == NO_ERROR; u = u->next)
    {
      error = set_referenced_subclasses (u->op);
    }

exit_on_end:
  return error;

exit_on_error:
  CHECK_EXIT_ERROR (error);
  goto exit_on_end;
}


/*
 * check_referenced_domain - check for OBJECT domain as referenced
 *    return: true, if found referened OBJECT domain. false, if not
 *            found referenced OBJECT domain.
 *    dom_list(in): domain list of class attributes
 *    set_cls_ref(in): for true, do checking. for false, do marking
 *    num_cls_refp(out): number of referenced classes.
 * Note:
 *    for referenced CLASS domain, mark the CLASS and set the number of
 *    referenced classes.
 */
static bool
check_referenced_domain (DB_DOMAIN * dom_list, bool set_cls_ref, int *num_cls_refp)
{
  bool found_object_dom;
  DB_DOMAIN *dom;
  DB_TYPE type;
  DB_OBJECT *class_;

  found_object_dom = false; /* init */

  for (dom = dom_list; dom && !found_object_dom; dom = db_domain_next (dom))
    {
      type = TP_DOMAIN_TYPE (dom);
      switch (type)
    {
    case DB_TYPE_OBJECT:
      class_ = db_domain_class (dom);
      if (class_ == NULL)
        {
          return true;  /* found object domain */
        }

      *num_cls_refp += 1;   /* increase number of reference to class */

      if (set_cls_ref)
        {
          if (set_referenced_subclasses (class_) != NO_ERROR)
        {
          /* cause error - currently, not happened */
          return true;
        }
        }
      break;
    case DB_TYPE_SET:
    case DB_TYPE_MULTISET:
    case DB_TYPE_SEQUENCE:
      found_object_dom = check_referenced_domain (db_domain_set (dom), set_cls_ref, num_cls_refp);
      break;
    default:
      break;
    }
    }
  return found_object_dom;
}


static bool
check_include_object_domain (DB_DOMAIN * dom_list, DB_TYPE * db_type)
{
  DB_DOMAIN *dom;

  for (dom = dom_list; dom; dom = db_domain_next (dom))
    {
      switch (TP_DOMAIN_TYPE (dom))
    {
    case DB_TYPE_OID:
    case DB_TYPE_OBJECT:
      *db_type = TP_DOMAIN_TYPE (dom);
      return true;

    case DB_TYPE_SET:
    case DB_TYPE_MULTISET:
    case DB_TYPE_SEQUENCE:
      if (check_include_object_domain (db_domain_set (dom), db_type))
        {
          return true;
        }
      break;

    default:
      break;
    }
    }
  return false;
}


/*
 * extractobjects_cleanup - do cleanup task
 *    return: void
 */
static void
extractobjects_cleanup ()
{
  close_object_file ();

  if (obj_table != NULL)
    {
      if (debug_flag)
    {
      fh_dump (obj_table);
    }
      fh_destroy (obj_table);
    }
  if (cl_table != NULL)
    fh_destroy (cl_table);

  free_and_init (class_requested);
  free_and_init (class_referenced);
  free_and_init (class_processed);
  return;
}

/*
 * extractobjects_term_handler - extractobject terminate handler
 *    return: void
 *    sig(in): not used
 */
static void
extractobjects_term_handler (int sig)
{
  static volatile int hit_signal = 0;
  int hit = ++hit_signal;
  if (hit != 1)
    {
      return;
    }

  error_occurred = true;
  extractor_thread_proc_terminate = true;
  usleep (1000);
  writer_thread_proc_terminate = true;
  usleep (100);

  extractobjects_cleanup ();
  /* terminate a program */
  _exit (1);
}

/*
 * mark_referenced_domain - mark given SM_CLASS closure
 *    return: true if no error, false otherwise
 *    class_ptr(in): SM_CLASS
 *    num_set(out): amortized marking number of SM_CLASS closure
 */
static bool
mark_referenced_domain (SM_CLASS * class_ptr, int *num_set)
{
  SM_ATTRIBUTE *attribute;

  if (class_ptr == NULL)
    return true;

  for (attribute = class_ptr->shared; attribute != NULL; attribute = (SM_ATTRIBUTE *) attribute->header.next)
    {
      if (check_referenced_domain (attribute->domain, true /* do marking */ , num_set))
    {
      return false;
    }
    }

  for (attribute = class_ptr->class_attributes; attribute != NULL; attribute = (SM_ATTRIBUTE *) attribute->header.next)
    {
      if (check_referenced_domain (attribute->domain, true /* do marking */ , num_set))
    {
      return false;
    }
    }

  for (attribute = class_ptr->ordered_attributes; attribute; attribute = attribute->order_link)
    {
      if (attribute->header.name_space != ID_ATTRIBUTE)
    {
      continue;
    }
      if (check_referenced_domain (attribute->domain, true /* do marking */ , num_set))
    {
      return false;
    }
    }
  return true;
}

/*
 * extract_objects - dump the database in loader format.
 *    return: 0 for success. 1 for error
 *    exec_name(in): utility name
 */
int
extract_objects (extract_context & ctxt, const char *output_dirname, int nthreads, int sampling_records,
         bool enhanced_estimates)
{
  int i, error;
  HFID *hfid;
  int64_t est_objects = 0;
  int cache_size;
  SM_CLASS *class_ptr;
  const char **cptr;
  int status = 0;
  int num_unload_classes = 0;
  DB_OBJECT **unload_class_table = NULL;
  bool has_obj_ref;
  int num_cls_ref;
  SM_ATTRIBUTE *attribute;
  void (*prev_intr_handler) (int sig);
  void (*prev_term_handler) (int sig);
#if !defined (WINDOWS)
  void (*prev_quit_handler) (int sig);
#endif
  int64_t total_objects, failed_objects;
  LOG_LSA lsa;
  char unloadlog_filename[PATH_MAX];
  char owner_name[DB_MAX_IDENTIFIER_LENGTH] = { '\0' };
  char *class_name = NULL;
  char owner_str[DB_MAX_USER_LENGTH + 4] = { '\0' };
  TEXT_OUTPUT *obj_out = NULL;

  // set sampling mode
  g_sampling_records = sampling_records;

  /* register new signal handlers */
  prev_intr_handler = os_set_signal_handler (SIGINT, extractobjects_term_handler);
  prev_term_handler = os_set_signal_handler (SIGTERM, extractobjects_term_handler);
#if !defined(WINDOWS)
  prev_quit_handler = os_set_signal_handler (SIGQUIT, extractobjects_term_handler);
#endif

  if (cached_pages <= 0)
    {
      fprintf (stderr,
           msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_UNLOADDB, UNLOADDB_MSG_INVALID_CACHED_PAGES));
      return 1;
    }
  if (page_size < (ssize_t) (sizeof (OID) + sizeof (int)))
    {
      fprintf (stderr,
           msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_UNLOADDB, UNLOADDB_MSG_INVALID_CACHED_PAGE_SIZE));
      return 1;
    }

  /*
   * Open output file
   */
  if (output_dirname == NULL)
    output_dirname = ".";
  if (strlen (output_dirname) > PATH_MAX - 8)
    {
      fprintf (stderr, msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_UNLOADDB, UNLOADDB_MSG_INVALID_DIR_NAME));
      return 1;
    }

  if (init_thread_param (output_dirname, nthreads) == false)
    {
      status = 1;
      goto end;
    }

  obj_out = &(g_thr_param[0].text_output);

  if (!datafile_per_class)
    {
      if (open_object_file (ctxt, output_dirname, NULL) == false)
    {
      status = 1;
      goto end;
    }
    }


  /*
   * The user indicates which classes are to be processed by
   * using -i with a file that contains a list of classes.
   * If the -i option is not used, it means process all classes.
   * Thus if input_filename is null, it means process all classes.
   * Three bit arrays are allocated to indicate whether a class
   * is requested, is referenced or is processed.  The index
   * into these arrays is the same as the index into class_table->mops.
   */
  if ((unload_class_table = (DB_OBJECT **) malloc (DB_SIZEOF (void *) * class_table->num)) == NULL)
    {
      status = 1;
      goto end;
    }
  for (i = 0; i < class_table->num; ++i)
    {
      unload_class_table[i] = NULL;
    }
  if ((class_requested = (char *) malloc ((class_table->num + 7) / 8)) == NULL)
    {
      status = 1;
      goto end;
    }
  if ((class_referenced = (char *) malloc ((class_table->num + 7) / 8)) == NULL)
    {
      status = 1;
      goto end;
    }
  if ((class_processed = (char *) malloc ((class_table->num + 7) / 8)) == NULL)
    {
      status = 1;
      goto end;
    }

  memset (class_requested, 0, (class_table->num + 7) / 8);
  memset (class_referenced, 0, (class_table->num + 7) / 8);
  memset (class_processed, 0, (class_table->num + 7) / 8);

  /*
   * Create the class hash table
   * Its purpose is to hash a class OID to the index into the
   * class_table->mops array.
   */
  cl_table = fh_create ("class hash", 4096, 1024, 4, NULL, FH_OID_KEY, DB_SIZEOF (int), oid_hash, oid_compare_equals);
  if (cl_table == NULL)
    {
      status = 1;
      goto end;
    }

  has_obj_ref = false;      /* init */
  num_cls_ref = 0;      /* init */

  /*
   * Total the number of objects & mark requested classes.
   */
#if defined(CUBRID_DEBUG) || defined(CUBRID_DEBUG_TEST)
  fprintf (stderr, "----- all class dump -----\n");
#endif /* CUBRID_DEBUG */
  for (i = 0; i < class_table->num; i++)
    {
      if (WS_IS_DELETED (class_table->mops[i]) || class_table->mops[i] == sm_Root_class_mop
      || db_is_vclass (class_table->mops[i]))
    {
      continue;
    }

      error = au_fetch_class (class_table->mops[i], NULL, AU_FETCH_READ, AU_SELECT);
      if (error != NO_ERROR)
    {
      continue;
    }

      ws_find (class_table->mops[i], (MOBJ *) (&class_ptr));
      if (class_ptr == NULL)
    {
      status = 1;
      goto end;
    }

      for (cptr = prohibited_classes; *cptr; ++cptr)
    {
      if (strcmp (*cptr, sm_ch_name ((MOBJ) class_ptr)) == 0)
        {
          break;
        }
    }
      if (*cptr == NULL)
    {
#if defined(CUBRID_DEBUG) || defined(CUBRID_DEBUG_TEST)
      fprintf (stderr, "%s%s%s\n", PRINT_IDENTIFIER (sm_ch_name ((MOBJ) class_ptr)));
#endif /* CUBRID_DEBUG */

      SPLIT_USER_SPECIFIED_NAME (sm_ch_name ((MOBJ) class_ptr), owner_name, class_name);

      if ((ctxt.is_dba_user == false && ctxt.is_dba_group_member == false)
          && strcasecmp (owner_name, ctxt.login_user) != 0)
        {
          continue;
        }

      fh_put (cl_table, ws_oid (class_table->mops[i]), &i);
      if (input_filename)
        {
          if (is_req_class (class_table->mops[i]))
        {
          MARK_CLASS_REQUESTED (i);
        }
          else if (!required_class_only)
        {
          error = sm_is_system_class (class_table->mops[i]);
          if (error < 0)
            {
              status = 1;
              goto end;
            }
          if (error > 0)
            {
              MARK_CLASS_REQUESTED (i);
              error = NO_ERROR;
            }
        }
        }
      else
        {
          MARK_CLASS_REQUESTED (i);
        }

      if (!datafile_per_class && (!required_class_only || IS_CLASS_REQUESTED (i)))
        {
          PRINT_OWNER_NAME (owner_name, (ctxt.is_dba_user || ctxt.is_dba_group_member), owner_str,
                sizeof (owner_str));

          if (text_print (obj_out, NULL, 0, "%cid %s%s%s%s %d\n", '%', owner_str, PRINT_IDENTIFIER (class_name), i)
          != NO_ERROR)
        {
          status = 1;
          goto end;
        }
        }

      if (IS_CLASS_REQUESTED (i))
        {
          if (!datafile_per_class)
        {
          if (!has_obj_ref)
            {       /* not found object domain */
              for (attribute = class_ptr->shared; attribute != NULL;
               attribute = (SM_ATTRIBUTE *) attribute->header.next)
            {
              /* false -> don't set */
              if ((has_obj_ref = check_referenced_domain (attribute->domain, false, &num_cls_ref)) == true)
                {
#if defined(CUBRID_DEBUG) || defined(CUBRID_DEBUG_TEST)
                  fprintf (stderr, "found OBJECT domain: %s%s%s->%s\n",
                       PRINT_IDENTIFIER (sm_ch_name ((MOBJ) class_ptr)), db_attribute_name (attribute));
#endif /* CUBRID_DEBUG */
                  break;
                }
            }
            }

          if (!has_obj_ref)
            {       /* not found object domain */
              for (attribute = class_ptr->class_attributes; attribute != NULL;
               attribute = (SM_ATTRIBUTE *) attribute->header.next)
            {
              /* false -> don't set */
              if ((has_obj_ref = check_referenced_domain (attribute->domain, false, &num_cls_ref)) == true)
                {
#if defined(CUBRID_DEBUG) || defined(CUBRID_DEBUG_TEST)
                  fprintf (stderr, "found OBJECT domain: %s%s%s->%s\n",
                       PRINT_IDENTIFIER (sm_ch_name ((MOBJ) class_ptr)), db_attribute_name (attribute));
#endif /* CUBRID_DEBUG */
                  break;
                }
            }
            }

          if (!has_obj_ref)
            {       /* not found object domain */
              for (attribute = class_ptr->ordered_attributes; attribute; attribute = attribute->order_link)
            {
              if (attribute->header.name_space != ID_ATTRIBUTE)
                {
                  continue;
                }
              has_obj_ref = check_referenced_domain (attribute->domain, false
                                 /* don't set */ ,
                                 &num_cls_ref);
              if (has_obj_ref == true)
                {
#if defined(CUBRID_DEBUG) || defined(CUBRID_DEBUG_TEST)
                  fprintf (stderr, "found OBJECT domain: %s%s%s->%s\n",
                       PRINT_IDENTIFIER (sm_ch_name ((MOBJ) class_ptr)), db_attribute_name (attribute));
#endif /* CUBRID_DEBUG */
                  break;
                }
            }
            }
        }
          unload_class_table[num_unload_classes] = class_table->mops[i];
          num_unload_classes++;
        }

      if (IS_CLASS_REQUESTED (i))
        {
          hfid = sm_ch_heap ((MOBJ) class_ptr);
          if (!HFID_IS_NULL (hfid))
        {
          if (get_estimated_objs (hfid, &est_objects, enhanced_estimates) < 0)
            {
              status = 1;
              goto end;
            }
        }
        }
    }
    }

  OR_PUT_NULL_OID (&null_oid);

#if defined(CUBRID_DEBUG) || defined(CUBRID_DEBUG_TEST)
  fprintf (stderr, "has_obj_ref = %d, num_cls_ref = %d\n", has_obj_ref, num_cls_ref);
#endif /* CUBRID_DEBUG */

  if (has_obj_ref || num_cls_ref > 0)
    {               /* found any referenced domain */
      int num_set;

      num_set = 0;      /* init */

      for (i = 0; i < class_table->num; i++)
    {
      if (!IS_CLASS_REQUESTED (i))
        {
          continue;
        }

      /* check for emptyness, but not implemented NEED FUTURE WORk */

      if (has_obj_ref)
        {
          MARK_CLASS_REFERENCED (i);
          continue;
        }

      ws_find (class_table->mops[i], (MOBJ *) (&class_ptr));
      if (class_ptr == NULL)
        {
          status = 1;
          goto end;
        }

      if (mark_referenced_domain (class_ptr, &num_set) == false)
        {
          status = 1;
          goto end;
        }


    }           /* for (i = 0; i < class_table->num; i++) */

      if (has_obj_ref)
    {
      ;         /* nop */
    }
      else
    {
      if (num_cls_ref != num_set)
        {
#if defined(CUBRID_DEBUG) || defined(CUBRID_DEBUG_TEST)
          fprintf (stderr, "num_cls_ref = %d, num_set = %d\n", num_cls_ref, num_set);
#endif /* CUBRID_DEBUG */
          status = 1;
          goto end;
        }
    }
    }

#if defined(CUBRID_DEBUG) || defined(CUBRID_DEBUG_TEST)
  {
    int total_req_cls = 0;
    int total_ref_cls = 0;

    fprintf (stderr, "----- referenced class dump -----\n");
    for (i = 0; i < class_table->num; i++)
      {
    if (!IS_CLASS_REQUESTED (i))
      {
        continue;
      }
    total_req_cls++;
    if (IS_CLASS_REFERENCED (i))
      {
        ws_find (class_table->mops[i], (MOBJ *) (&class_ptr));
        if (class_ptr == NULL)
          {
        status = 1;
        goto end;
          }
        SPLIT_USER_SPECIFIED_NAME (sm_ch_name ((MOBJ) class_ptr), owner_name, class_name);
        fprintf (stderr, "%s%s%s.%s%s%s\n", PRINT_IDENTIFIER (owner_name), PRINT_IDENTIFIER (class_name));
        total_ref_cls++;
      }
      }
    fprintf (stderr, "class_table->num = %d, total_req_cls = %d, total_ref_cls = %d\n", class_table->num,
         total_req_cls, total_ref_cls);
  }
#endif /* CUBRID_DEBUG */

  /*
   * Lock all unloaded classes with IS_LOCK
   *   - If there is only view, num_unload_classes can be 0.
   */
  if (num_unload_classes
      && locator_fetch_set (num_unload_classes, unload_class_table, DB_FETCH_READ, DB_FETCH_READ, true) == NULL)
    {
      status = 1;
      goto end;
    }

  locator_get_append_lsa (&lsa);

  /*
   * Estimate the number of objects.
   */

  if (est_size == 0)
    {
      est_size = est_objects;
    }

  cache_size = cached_pages * page_size / (DB_SIZEOF (OID) + DB_SIZEOF (int));
  est_size = est_size > cache_size ? est_size : cache_size;

  /*
   * Create the hash table
   */
  obj_table =
    fh_create ("object hash", est_size, page_size, cached_pages, hash_filename, FH_OID_KEY, DB_SIZEOF (int),
           oid_hash, oid_compare_equals);

  if (obj_table == NULL)
    {
      status = 1;
      goto end;
    }

  /*
   * Dump the object definitions
   */
  total_approximate_class_objects = est_objects;

  if (create_filename
      (ctxt.output_dirname, ctxt.output_prefix, "_unloaddb.log", unloadlog_filename, sizeof (unloadlog_filename)) != 0)
    {
      util_log_write_errid (MSGCAT_UTIL_GENERIC_INVALID_ARGUMENT);
      status = 1;
      goto end;
    }
#if !defined(MULTI_PROCESSING_UNLOADDB_WITH_FORK)
  unloadlog_file = fopen (unloadlog_filename, "w+");
  if (unloadlog_file != NULL)
    {
      fprintf (unloadlog_file, HEADER_FORMAT, "Class Name", "Total Instances");
    }
#else
  p_unloadlog_filename = unloadlog_filename;
  unload_log_write (p_unloadlog_filename, HEADER_FORMAT, "Class Name", "Total Instances");
#endif

  if (verbose_flag)
    {
      fprintf (stdout, HEADER_FORMAT, "Class Name", "Total Instances");
    }

  error_occurred = false;
  if (!datafile_per_class)
    {
      error = text_print_request_flush (&(g_thr_param[0].text_output), true);
      if (error != NO_ERROR)
    {
      status = 1;
      goto end;
    }
    }
  else
    {
      assert (g_thr_param[0].text_output.head_ptr == NULL || g_thr_param[0].text_output.head_ptr->count <= 0);
    }

  do
    {
      for (i = 0; i < class_table->num; i++)
    {
      if (!WS_IS_DELETED (class_table->mops[i]) && class_table->mops[i] != sm_Root_class_mop)
        {
          int ret_val;

          if (datafile_per_class && IS_CLASS_REQUESTED (i))
        {
          ws_find (class_table->mops[i], (MOBJ *) (&class_ptr));
          if (class_ptr == NULL)
            {
              status = 1;
              goto end;
            }

          if (open_object_file (ctxt, output_dirname, sm_ch_name ((MOBJ) class_ptr)) == false)
            {
              status = 1;
              goto end;
            }
        }

          ret_val = process_class (ctxt, i, nthreads);

          if (datafile_per_class && IS_CLASS_REQUESTED (i))
        {
          close_object_file ();
        }

          if (ret_val != NO_ERROR)
        {
          if (!ignore_err_flag)
            {
              status = 1;
              goto end;
            }
        }
        }
    }
    }
  while (!all_classes_processed ());

  total_objects = total_objects_atomic;
  failed_objects = failed_objects_atomic;
  if (failed_objects != 0)
    {
      status = 1;
      fprintf (stdout, msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_UNLOADDB, UNLOADDB_MSG_OBJECTS_FAILED),
           total_objects - failed_objects, total_objects);
#if !defined(MULTI_PROCESSING_UNLOADDB_WITH_FORK)
      if (unloadlog_file != NULL)
    {
      fprintf (unloadlog_file,
           msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_UNLOADDB, UNLOADDB_MSG_OBJECTS_FAILED),
           total_objects - failed_objects, total_objects);
    }
#else
      unload_log_write (p_unloadlog_filename,
            msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_UNLOADDB, UNLOADDB_MSG_OBJECTS_FAILED),
            total_objects - failed_objects, total_objects);
#endif
    }
  else if (verbose_flag)
    {
      fprintf (stdout, msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_UNLOADDB, UNLOADDB_MSG_OBJECTS_DUMPED),
           total_objects);
    }

#if !defined(MULTI_PROCESSING_UNLOADDB_WITH_FORK)
  if (unloadlog_file != NULL)
    {
      if (failed_objects == 0)
    {
      fprintf (unloadlog_file,
           msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_UNLOADDB, UNLOADDB_MSG_OBJECTS_DUMPED),
           total_objects);
    }
      fprintf (unloadlog_file, msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_UNLOADDB, UNLOADDB_MSG_LOG_LSA),
           lsa.pageid, lsa.offset);
    }
#else
  if (failed_objects == 0)
    {
      unload_log_write (p_unloadlog_filename,
            msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_UNLOADDB, UNLOADDB_MSG_OBJECTS_DUMPED),
            total_objects);
    }
  unload_log_write (p_unloadlog_filename,
            msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_UNLOADDB, UNLOADDB_MSG_LOG_LSA), lsa.pageid,
            lsa.offset);
#endif

  /* flush remaining buffer */

  if (!datafile_per_class)
    {
      close_object_file ();
    }

/* in case of both normal and error */
end:
#if !defined(MULTI_PROCESSING_UNLOADDB_WITH_FORK)
  if (unloadlog_file != NULL)
    {
      fclose (unloadlog_file);
    }
#endif
  /*
   * Cleanup
   */
  free_and_init (unload_class_table);
  quit_thread_param ();
  extractobjects_cleanup ();

  /* restore previous signal handlers */
  (void) os_set_signal_handler (SIGINT, prev_intr_handler);
  (void) os_set_signal_handler (SIGTERM, prev_term_handler);
#if !defined (WINDOWS)
  (void) os_set_signal_handler (SIGQUIT, prev_quit_handler);
#endif

  return (status);
}


/*
 * gauge_alarm_handler - signal handler
 *    return: void
 *    sig(in): singal number
 */
static void
gauge_alarm_handler (int sig)
{
  if (sig == SIGALRM)
    {
      int64_t class_objects = class_objects_atomic;
      int64_t total_objects = total_objects_atomic;
      int64_t total_approximate_class_objects_tmp = total_approximate_class_objects;

      if (class_objects > approximate_class_objects)
    {
      total_approximate_class_objects_tmp += (class_objects - approximate_class_objects);
    }

      fprintf (stdout, MSG_FORMAT "\r", gauge_class_name, class_objects,
           (class_objects > 0 && approximate_class_objects >= class_objects)
           ? (int) (100 * ((float) class_objects / (float) approximate_class_objects)) : 100,
           (int) (100 * ((float) total_objects / (float) total_approximate_class_objects_tmp)));
      fflush (stdout);
    }
  else
    {
      ;
    }
#if !defined(WINDOWS)
  alarm (GAUGE_INTERVAL);
#endif
  return;
}


static int
unload_printer (LC_COPYAREA * fetch_area, DESC_OBJ * desc_obj, TEXT_OUTPUT * obj_out)
{
  int i, error;
  RECDES recdes;        /* Record descriptor */
  LC_COPYAREA_MANYOBJS *mobjs;  /* Describe multiple objects in area */
  LC_COPYAREA_ONEOBJ *obj;  /* Describe on object in area */
#if defined(WINDOWS)
  struct _timeb timebuffer;
  time_t start = 0;
#endif

  error = NO_ERROR;
  mobjs = LC_MANYOBJS_PTR_IN_COPYAREA (fetch_area);
  obj = LC_START_ONEOBJ_PTR_IN_COPYAREA (mobjs);

  for (i = 0; i < mobjs->num_objs; ++i)
    {
      /*
       * Process all objects for a requested class, but
       * only referenced objects for a referenced class.
       */
      ++class_objects_atomic;
      ++total_objects_atomic;
      LC_RECDES_TO_GET_ONEOBJ (fetch_area, obj, &recdes);
      TIMER_BEGIN ((g_sampling_records >= 0), &(g_thr_param[obj_out->ref_thread_param_idx].wi_to_obj_str[0]));
      error = desc_disk_to_obj (g_uci->class_, g_uci->class_ptr, &recdes, desc_obj, true);
      TIMER_END ((g_sampling_records >= 0), &(g_thr_param[obj_out->ref_thread_param_idx].wi_to_obj_str[0]));
      if (error == NO_ERROR)
    {
      error = process_object (desc_obj, &obj->oid, g_uci->referenced_class, obj_out);
      if (error != NO_ERROR)
        {
          if (!ignore_err_flag)
        {
          break;
        }
          error = NO_ERROR;
        }
    }
      else
    {
      if (error == ER_TF_BUFFER_UNDERFLOW)
        {
          break;
        }
      ++failed_objects_atomic;
    }
      obj = LC_NEXT_ONEOBJ_PTR_IN_COPYAREA (obj);
#if defined(WINDOWS)
      if (verbose_flag && (i % 10 == 0))
    {
      _ftime (&timebuffer);
      if (start == 0)
        {
          start = timebuffer.time;
        }
      else
        {
          if ((timebuffer.time - start) > GAUGE_INTERVAL)
        {
          gauge_alarm_handler (SIGALRM);
          start = timebuffer.time;
        }
        }
    }
#endif
    }

  return error;
}

static THREAD_RET_T THREAD_CALLING_CONVENTION
unload_extractor_thread (void *param)
{
  UNLD_THR_PARAM *parg = (UNLD_THR_PARAM *) param;
  int thr_ret = NO_ERROR;
  pthread_t tid = pthread_self ();
  TEXT_OUTPUT *obj_out = &(parg->text_output);
  DESC_OBJ *desc_obj = make_desc_obj (g_uci->class_ptr, g_pre_alloc_varchar_size);
  if (desc_obj == NULL)
    {
      thr_ret = ER_FAILED;
    }
  else
    {
      LC_COPYAREA_NODE *node = NULL;
      cuberr::context * er_context_p;

      // we need to register a context
      er_context_p = new cuberr::context ();
      er_context_p->register_thread_local ();

      while (extractor_thread_proc_terminate == false)
    {
      node = g_uci->cparea_lst_ref->get ();
      if (node)
        {
          thr_ret = unload_printer (node->lc_copy_area, desc_obj, obj_out);
          g_uci->cparea_lst_ref->add_freelist (node);
          if (thr_ret != NO_ERROR)
        {
          break;
        }
        }
      else if (extractor_thread_proc_terminate == false)
        {
          TIMER_BEGIN ((g_sampling_records >= 0), &(parg->wi_get_list));

          pthread_mutex_lock (&g_uci->mtx);
          while (g_uci->signaled == false && g_uci->broadcasted == false)   /* check spurious wakeup */
        {
          pthread_cond_wait (&g_uci->cond, &g_uci->mtx);
        }
          g_uci->signaled = false;
          pthread_mutex_unlock (&g_uci->mtx);

          TIMER_END ((g_sampling_records >= 0), &(parg->wi_get_list));
        }
    }

      node = g_uci->cparea_lst_ref->get ();
      while (node)
    {
      if (thr_ret == NO_ERROR)
        {
          thr_ret = unload_printer (node->lc_copy_area, desc_obj, obj_out);
        }
      g_uci->cparea_lst_ref->add_freelist (node);
      node = g_uci->cparea_lst_ref->get ();
    }

      er_context_p->deregister_thread_local ();
      delete er_context_p;
      er_context_p = NULL;

      desc_free (desc_obj);
    }

  if (thr_ret != NO_ERROR)
    {
      error_occurred = true;
    }

#if defined(WINDOWS)
  pthread_exit ((THREAD_RET_T) thr_ret);
#else
  pthread_exit ((THREAD_RET_T) & thr_ret);
#endif
}


int
unload_fetcher (LC_FETCH_VERSION_TYPE fetch_type)
{
  int i, error = NO_ERROR;
  int nobjects = 0;
  int nfetched = -1;
  OID last_oid;
  LC_COPYAREA *fetch_area;  /* Area where objects are received */
  LOCK lock = IS_LOCK;      /* Lock to acquire for the above purpose */
  TEXT_OUTPUT *obj_out = NULL;
  DESC_OBJ *desc_obj = NULL;
  OID *class_oid = g_uci->class_oid;
  HFID *hfid = g_uci->hfid;

  OID_SET_NULL (&last_oid);

  if (!g_multi_thread_mode)
    {
      obj_out = &(g_thr_param[0].text_output);
      desc_obj = make_desc_obj (g_uci->class_ptr, g_pre_alloc_varchar_size);
    }

  while ((nobjects != nfetched) && (error_occurred == false))
    {
      TIMER_BEGIN ((g_sampling_records >= 0), &(g_uci->wi_fetch));
      error = locator_fetch_all (hfid, &lock, fetch_type, class_oid, &nobjects, &nfetched, &last_oid, &fetch_area,
                 g_request_pages, g_parallel_process_cnt,
                 (g_parallel_process_idx - 1) /* to zero base */ );
      TIMER_END ((g_sampling_records >= 0), &(g_uci->wi_fetch));
      if (error == NO_ERROR)
    {
      if (fetch_area != NULL)
        {
          if (!g_multi_thread_mode)
        {
          error = unload_printer (fetch_area, desc_obj, obj_out);
          locator_free_copy_area (fetch_area);
          if (error != NO_ERROR)
            {
              break;
            }
        }
          else
        {
          g_uci->cparea_lst_ref->add (fetch_area, true);
          fetch_area = NULL;
          pthread_mutex_lock (&g_uci->mtx);
          g_uci->signaled = true;
          pthread_cond_signal (&g_uci->cond);
          pthread_mutex_unlock (&g_uci->mtx);
        }

          if (g_sampling_records > 0)
        {
          if (nfetched >= g_sampling_records)
            break;
        }
        }
      else
        {
          /* No more objects */
          break;
        }
    }
      else
    {
      /* some error was occurred */
      if (!ignore_err_flag)
        {
          break;
        }
      error = NO_ERROR;
      ++failed_objects_atomic;
    }
    }

  if (desc_obj)
    {
      desc_free (desc_obj);
    }

  if (error != NO_ERROR)
    {
      error_occurred = true;
    }

  return error;
}

static THREAD_RET_T THREAD_CALLING_CONVENTION
unload_writer_thread (void *param)
{
  pthread_t tid = pthread_self ();
  int thr_ret = NO_ERROR;
  int ret;

  while (writer_thread_proc_terminate == false)
    {
      ret = flushing_write_blk_queue ();
      if (ret < 0)
    {
      thr_ret = ER_IO_WRITE;
      break;
    }

      TIMER_BEGIN ((g_sampling_records >= 0), &wi_w_blk_getQ);
      usleep (100);
#if !defined(WINDOWS)
      if (ret == 0)
    {
      wi_w_blk_getQ.cnt--;
    }
#endif
      TIMER_END ((g_sampling_records >= 0), &wi_w_blk_getQ);
    }

  if (thr_ret == NO_ERROR)
    {
      ret = flushing_write_blk_queue ();
      if (ret < 0)
    {
      thr_ret = ER_IO_WRITE;
    }
    }

  if (thr_ret != NO_ERROR)
    {
      error_occurred = true;
    }

#if defined(WINDOWS)
  pthread_exit ((THREAD_RET_T) thr_ret);
#else
  pthread_exit ((THREAD_RET_T) & thr_ret);
#endif
}

int
print_object_header_for_class (extract_context & ctxt, SM_CLASS * class_ptr, OID * class_oid, TEXT_OUTPUT * obj_out)
{
  char owner_name[DB_MAX_IDENTIFIER_LENGTH] = { '\0' };
  char *class_name = NULL;
  char output_owner[DB_MAX_USER_LENGTH + 4] = { '\0' };
  SM_ATTRIBUTE *attribute;
  int v, error = NO_ERROR;

  v = 0;
  for (attribute = class_ptr->shared; attribute != NULL; attribute = (SM_ATTRIBUTE *) attribute->header.next)
    {
      if (DB_VALUE_TYPE (&attribute->default_value.value) == DB_TYPE_NULL)
    {
      continue;
    }
      if (v == 0)
    {
      SPLIT_USER_SPECIFIED_NAME (sm_ch_name ((MOBJ) class_ptr), owner_name, class_name);

      PRINT_OWNER_NAME (owner_name, (ctxt.is_dba_user || ctxt.is_dba_group_member), output_owner,
                sizeof (output_owner));

      CHECK_PRINT_ERROR (text_print
                 (obj_out, NULL, 0, "%cclass %s%s%s%s shared (%s%s%s", '%',
                  output_owner, PRINT_IDENTIFIER (class_name), PRINT_IDENTIFIER (attribute->header.name)));
    }
      else
    {
      CHECK_PRINT_ERROR (text_print (obj_out, NULL, 0, ", %s%s%s", PRINT_IDENTIFIER (attribute->header.name)));
    }

      ++v;
    }
  if (v)
    {
      CHECK_PRINT_ERROR (text_print (obj_out, ")\n", 2, NULL));
    }

  v = 0;
  for (attribute = class_ptr->shared; attribute != NULL; attribute = (SM_ATTRIBUTE *) attribute->header.next)
    {
      if (DB_VALUE_TYPE (&attribute->default_value.value) == DB_TYPE_NULL)
    {
      continue;
    }
      if (v)
    {
      CHECK_PRINT_ERROR (text_print (obj_out, " ", 1, NULL));
    }
      error = process_value (&attribute->default_value.value, obj_out);
      if (error != NO_ERROR)
    {
      if (!ignore_err_flag)
        {
          goto exit_on_error;
        }
      error = NO_ERROR;
    }

      ++v;
    }
  if (v)
    {
      CHECK_PRINT_ERROR (text_print (obj_out, "\n", 1, NULL));
    }

  v = 0;
  for (attribute = class_ptr->class_attributes; attribute != NULL; attribute = (SM_ATTRIBUTE *) attribute->header.next)
    {
      if (DB_VALUE_TYPE (&attribute->default_value.value) == DB_TYPE_NULL)
    {
      continue;
    }
      if (v == 0)
    {
      SPLIT_USER_SPECIFIED_NAME (sm_ch_name ((MOBJ) class_ptr), owner_name, class_name);

      PRINT_OWNER_NAME (owner_name, (ctxt.is_dba_user || ctxt.is_dba_group_member), output_owner,
                sizeof (output_owner));

      CHECK_PRINT_ERROR (text_print
                 (obj_out, NULL, 0, "%cclass %s%s%s%s class (%s%s%s", '%',
                  output_owner, PRINT_IDENTIFIER (class_name), PRINT_IDENTIFIER (attribute->header.name)));
    }
      else
    {
      CHECK_PRINT_ERROR (text_print (obj_out, NULL, 0, ", %s%s%s", PRINT_IDENTIFIER (attribute->header.name)));
    }
      ++v;
    }
  if (v)
    {
      CHECK_PRINT_ERROR (text_print (obj_out, ")\n", 2, NULL));
    }

  v = 0;
  for (attribute = class_ptr->class_attributes; attribute != NULL; attribute = (SM_ATTRIBUTE *) attribute->header.next)
    {
      if (DB_VALUE_TYPE (&attribute->default_value.value) == DB_TYPE_NULL)
    {
      continue;
    }
      if (v)
    {
      CHECK_PRINT_ERROR (text_print (obj_out, " ", 1, NULL));
    }
      if ((error = process_value (&attribute->default_value.value, obj_out)) != NO_ERROR)
    {
      if (!ignore_err_flag)
        {
          goto exit_on_error;
        }
      error = NO_ERROR;
    }

      ++v;
    }

  SPLIT_USER_SPECIFIED_NAME (sm_ch_name ((MOBJ) class_ptr), owner_name, class_name);

  PRINT_OWNER_NAME (owner_name, (ctxt.is_dba_user || ctxt.is_dba_group_member), output_owner, sizeof (output_owner));

  CHECK_PRINT_ERROR (text_print (obj_out, NULL, 0, (v) ? "\n%cclass %s%s%s%s (" /* new line */
                 : "%cclass %s%s%s%s (", '%', output_owner, PRINT_IDENTIFIER (class_name)));

  v = 0;
  attribute = class_ptr->ordered_attributes;
  while (attribute)
    {
      if (attribute->header.name_space == ID_ATTRIBUTE)
    {
      CHECK_PRINT_ERROR (text_print (obj_out, NULL, 0, (v) ? " %s%s%s"  /* space */
                     : "%s%s%s", PRINT_IDENTIFIER (attribute->header.name)));
      ++v;
    }
      attribute = (SM_ATTRIBUTE *) attribute->order_link;
    }
  CHECK_PRINT_ERROR (text_print (obj_out, ")\n", 2, NULL));

exit_on_error:
  return error;
}

/*
 * process_class - dump one class in loader format
 *    return: NO_ERROR, if successful, error number, if not successful.
 *    cl_no(in): class object index for class_table
 */
static int
process_class (extract_context & ctxt, int cl_no, int nthreads)
{
  int error = NO_ERROR;
  DB_OBJECT *class_ = class_table->mops[cl_no];
  int i = 0;
  SM_CLASS *class_ptr;
  HFID *hfid;
  OID *class_oid;
  int requested_class = 0;
  int referenced_class = 0;
  void (*prev_handler) (int sig) = NULL;
  unsigned int prev_alarm = 0;
  int64_t class_objects = 0;
  int64_t total_objects = 0;
#if defined(WINDOWS)
  struct _timeb timebuffer;
  time_t start = 0;
#endif
  int total;
  bool enabled_alarm = false;
  LC_FETCH_VERSION_TYPE fetch_type = latest_image_flag ? LC_FETCH_CURRENT_VERSION : LC_FETCH_MVCC_VERSION;
  UNLD_CLASS_PARAM unld_cls_info;
  pthread_t writer_tid;
  int *retval = NULL;
  class copyarea_list c_cparea_lst_class;

  /*
   * Only process classes that were requested or classes that were
   * referenced via requested classes.
   */
  if (IS_CLASS_PROCESSED (cl_no))
    {
      return NO_ERROR;      /* do nothing successfully */
    }

  if (IS_CLASS_REQUESTED (cl_no))
    {
      requested_class = 1;
    }
  if (IS_CLASS_REFERENCED (cl_no))
    {
      referenced_class = 1;
    }

  if (!requested_class && !referenced_class)
    {
      return NO_ERROR;      /* do nothing successfully */
    }

  class_objects_atomic = 0;
  MARK_CLASS_PROCESSED (cl_no);

  /* Get the class data */
  ws_find (class_, (MOBJ *) (&class_ptr));
  if (class_ptr == NULL)
    {
      goto exit_on_error;
    }

  TIMER_CLEAR (&wi_w_blk_getQ);
  TIMER_CLEAR (&wi_write_file);
  TIMER_CLEAR (&wi_unload_class);
  TIMER_BEGIN ((g_sampling_records >= 0), &wi_unload_class);

  if (nthreads > 0)
    {
      for (i = 0; i < class_ptr->att_count; i++)
    {
      DB_TYPE db_type_in;
      DB_TYPE db_type = class_ptr->attributes[i].type->get_id ();
      switch (db_type)
        {
        case DB_TYPE_OID:
        case DB_TYPE_OBJECT:
          fprintf (stderr, "warning: %s%s%s has %s type.\n", PRINT_IDENTIFIER (sm_ch_name ((MOBJ) class_ptr)),
               db_get_type_name (db_type));
          fprintf (stderr, "So for class %s%s%s, '--thread-count' option is ignored.\n",
               PRINT_IDENTIFIER (sm_ch_name ((MOBJ) class_ptr)));
          fflush (stderr);
          // Notice: In this case, Do NOT use multi-threading!
          nthreads = 0;
          break;

        case DB_TYPE_SET:
        case DB_TYPE_MULTISET:
        case DB_TYPE_SEQUENCE:
          if (check_include_object_domain (class_ptr->attributes[i].domain, &db_type_in))
        {
          fprintf (stderr, "warning: %s%s%s has %s type with %s type.\n",
               PRINT_IDENTIFIER (sm_ch_name ((MOBJ) class_ptr)), db_get_type_name (db_type),
               db_get_type_name (db_type_in));
          fprintf (stderr, "So for class %s%s%s, '--thread-count' option is ignored.\n",
               PRINT_IDENTIFIER (sm_ch_name ((MOBJ) class_ptr)));
          fflush (stderr);
          // Notice: In this case, Do NOT use multi-threading!
          nthreads = 0;
        }
          break;

        default:
          break;
        }
    }
    }

  /* Before outputting individual records, common information needs to be output.
   * Let's print this information to a file immediately.
   */
  g_multi_thread_mode = false;

  class_oid = ws_oid (class_);

  error = print_object_header_for_class (ctxt, class_ptr, class_oid, &(g_thr_param[0].text_output));
  if (error != NO_ERROR)
    {
      goto exit_on_error;
    }

  /* Find the heap where the instances are stored */
  hfid = sm_ch_heap ((MOBJ) class_ptr);
  if (hfid->vfid.fileid == NULL_FILEID)
    {
      goto exit_on_end;
    }

  /* Flush all the instances */
  if (locator_flush_all_instances (class_, DONT_DECACHE) != NO_ERROR)
    {
      goto exit_on_end;
    }

  /* Now start fetching all the instances */
  approximate_class_objects = 0;
  if (get_estimated_objs (hfid, &approximate_class_objects, false) < 0)
    {
      if (!ignore_err_flag)
    goto exit_on_error;
    }

  if (verbose_flag)
    {
      gauge_class_name = (char *) sm_ch_name ((MOBJ) class_ptr);
#if !defined (WINDOWS)
      prev_handler = os_set_signal_handler (SIGALRM, gauge_alarm_handler);
      prev_alarm = alarm (GAUGE_INTERVAL);
      enabled_alarm = true;
#endif
    }

  error_occurred = false;
  error = text_print_request_flush (&(g_thr_param[0].text_output), true);
  if (error != NO_ERROR)
    {
      goto exit_on_end;
    }

  extractor_thread_proc_terminate = false;
  writer_thread_proc_terminate = false;
  g_multi_thread_mode = (nthreads > 0) ? true : false;
  TIMER_CLEAR (&(g_thr_param[0].wi_get_list));
  TIMER_CLEAR (&(g_thr_param[0].wi_add_Q));

  TIMER_CLEAR (&(g_thr_param[0].wi_to_obj_str[0]));
  TIMER_CLEAR (&(g_thr_param[0].wi_to_obj_str[1]));

  unld_cls_info.hfid = hfid;
  unld_cls_info.class_oid = class_oid;
  unld_cls_info.class_ptr = class_ptr;
  unld_cls_info.class_ = class_;
  unld_cls_info.pctxt = &ctxt;
  unld_cls_info.referenced_class = referenced_class;
  unld_cls_info.signaled = false;
  unld_cls_info.broadcasted = false;
#if !defined(WINDOWS)
  memset (&(unld_cls_info.wi_fetch), 0x00, sizeof (S_WAITING_INFO));
#endif

  c_cparea_lst_class.set_max_list (max_fetched_copyarea_list);
  unld_cls_info.cparea_lst_ref = &c_cparea_lst_class;
  g_uci = &unld_cls_info;


  if (g_multi_thread_mode)
    {
      if (pthread_mutex_init (&unld_cls_info.mtx, NULL) != 0)
    {
      error = ER_FAILED;
      goto exit_on_end;
    }
      else if (pthread_cond_init (&unld_cls_info.cond, NULL) != 0)
    {
      pthread_mutex_destroy (&unld_cls_info.mtx);
      error = ER_FAILED;
      goto exit_on_end;
    }

      for (i = 0; i < nthreads; i++)
    {
      g_thr_param[i].thread_idx = i;
      g_thr_param[i].tid = INVALID_THREAD_ID;
      assert (g_thr_param[i].text_output.ref_thread_param_idx == i);

      TIMER_CLEAR (&(g_thr_param[i].wi_get_list));
      TIMER_CLEAR (&(g_thr_param[i].wi_add_Q));

      TIMER_CLEAR (&(g_thr_param[i].wi_to_obj_str[0]));
      TIMER_CLEAR (&(g_thr_param[i].wi_to_obj_str[1]));

      if (pthread_create (&(g_thr_param[i].tid), NULL, unload_extractor_thread, (void *) (g_thr_param + i)) != 0)
        {
          perror ("pthread_create()\n");
          _exit (1);
        }
    }

      if (pthread_create (&writer_tid, NULL, unload_writer_thread, NULL) != 0)
    {
      perror ("pthread_create()\n");
      _exit (1);
    }
    }

  YIELD_THREAD ();
  unload_fetcher (fetch_type);

  if (!g_multi_thread_mode)
    {
      assert (g_thr_param[0].text_output.ref_thread_param_idx == 0);
      error = text_print_request_flush (&(g_thr_param[0].text_output), true);
    }
  else
    {
      extractor_thread_proc_terminate = true;

      pthread_mutex_lock (&unld_cls_info.mtx);
      unld_cls_info.broadcasted = true;
      pthread_cond_broadcast (&unld_cls_info.cond);
      pthread_mutex_unlock (&unld_cls_info.mtx);
      YIELD_THREAD ();

      for (i = 0; i < nthreads; i++)
    {
      int _err;
      void *retval;

      pthread_join (g_thr_param[i].tid, &retval);
      if ((_err = text_print_request_flush (&(g_thr_param[i].text_output), true)) != NO_ERROR)
        {
          error = _err;
        }
    }

      void *retval;
      writer_thread_proc_terminate = true;
      pthread_join (writer_tid, &retval);

      g_multi_thread_mode = false;

      pthread_cond_destroy (&unld_cls_info.cond);
      pthread_mutex_destroy (&unld_cls_info.mtx);
    }

  TIMER_END ((g_sampling_records >= 0), &wi_unload_class);

  if (error_occurred && error == NO_ERROR)
    {
      error = ER_FAILED;
    }

  class_objects = class_objects_atomic;
  total_approximate_class_objects += (class_objects - approximate_class_objects);

exit_on_end:
  class_objects = class_objects_atomic;
  total_objects = total_objects_atomic;
  if (total_objects >= total_approximate_class_objects)
    {
      total = 100;
    }
  else
    {
      total = (int) (100 * ((float) total_objects / (float) total_approximate_class_objects));
    }
  if (verbose_flag)
    {
#if !defined(WINDOWS)
      if (enabled_alarm)
    {
      alarm (prev_alarm);
      (void) os_set_signal_handler (SIGALRM, prev_handler);
    }
#endif

      fprintf (stdout, MSG_FORMAT "\n", sm_ch_name ((MOBJ) class_ptr), class_objects, 100, total);
      fflush (stdout);
    }

#if !defined(MULTI_PROCESSING_UNLOADDB_WITH_FORK)
  fprintf (unloadlog_file, MSG_FORMAT "\n", sm_ch_name ((MOBJ) class_ptr), class_objects, 100, total);
#else
  unload_log_write (p_unloadlog_filename, MSG_FORMAT "\n", sm_ch_name ((MOBJ) class_ptr), class_objects, 100, total);
#endif

  if (g_sampling_records >= 0)
    {
      print_monitoring_info (sm_ch_name ((MOBJ) class_ptr), nthreads);
    }
  return error;

exit_on_error:

  CHECK_EXIT_ERROR (error);
  return error;

}

/*
 * process_object - dump one object in loader format
 *    return: NO_ERROR, if successful, error number, if not successful.
 *    desc_obj(in): object data
 *    obj_oid(in): object oid
 *    referenced_class(in): is referenced ?
 */
static int
process_object (DESC_OBJ * desc_obj, OID * obj_oid, int referenced_class, TEXT_OUTPUT * obj_out)
{
  int error = NO_ERROR;
  SM_CLASS *class_ptr;
  SM_ATTRIBUTE *attribute;
  DB_VALUE *value;
  OID *class_oid;
  int data;
  int v = 0;

  TIMER_BEGIN ((g_sampling_records >= 0), &(g_thr_param[obj_out->ref_thread_param_idx].wi_to_obj_str[1]));
  class_ptr = desc_obj->class_;
  class_oid = ws_oid (desc_obj->classop);
  if (!datafile_per_class && referenced_class)
    {               /* need to hash OID */
      if (g_multi_thread_mode)
    {
      pthread_mutex_lock (&g_update_hash_cs_lock);
      update_hash (obj_oid, class_oid, &data);
      pthread_mutex_unlock (&g_update_hash_cs_lock);
    }
      else
    {
      update_hash (obj_oid, class_oid, &data);
    }

      if (debug_flag)
    {
      CHECK_PRINT_ERROR (text_print
                 (obj_out, NULL, 0, "%d/*%d.%d.%d*/: ", data, obj_oid->volid, obj_oid->pageid,
                  obj_oid->slotid));
    }
      else
    {
      CHECK_PRINT_ERROR (text_print (obj_out, NULL, 0, "%d: ", data));
    }
    }

  attribute = class_ptr->ordered_attributes;
  for (attribute = class_ptr->ordered_attributes; attribute; attribute = attribute->order_link)
    {

      if (attribute->header.name_space != ID_ATTRIBUTE)
    continue;

      if (v)
    CHECK_PRINT_ERROR (text_print (obj_out, " ", 1, NULL));

      value = &desc_obj->values[attribute->storage_order];

      if ((error = process_value (value, obj_out)) != NO_ERROR)
    {
      if (!ignore_err_flag)
        goto exit_on_error;
    }

      ++v;
    }
  CHECK_PRINT_ERROR (text_print (obj_out, "\n", 1, NULL));
  TIMER_END ((g_sampling_records >= 0), &(g_thr_param[obj_out->ref_thread_param_idx].wi_to_obj_str[1]));

  return text_print_request_flush (obj_out, false);

exit_on_error:
  CHECK_EXIT_ERROR (error);
  TIMER_END ((g_sampling_records >= 0), &(g_thr_param[obj_out->ref_thread_param_idx].wi_to_obj_str[1]));

  return error;

}

/*
 * process_set - dump a set in loader format
 *    return: NO_ERROR, if successful, error number, if not successful.
 *    set(in): set
 * Note:
 *    Should only get here for class and shared attributes that have
 *    default values.
 */
static int
process_set (DB_SET * set, TEXT_OUTPUT * obj_out)
{
  int error = NO_ERROR;
  SET_ITERATOR *it = NULL;
  DB_VALUE *element_value;
  int check_nelem = 0;

  CHECK_PRINT_ERROR (text_print (obj_out, "{", 1, NULL));

  it = set_iterate (set);
  while ((element_value = set_iterator_value (it)) != NULL)
    {

      if ((error = process_value (element_value, obj_out)) != NO_ERROR)
    {
      if (!ignore_err_flag)
        goto exit_on_error;
    }

      check_nelem++;

      if (set_iterator_next (it))
    {
      CHECK_PRINT_ERROR (text_print (obj_out, ", ", 2, NULL));
      if (check_nelem >= 10)
        {           /* set New-Line for each 10th elements */
          CHECK_PRINT_ERROR (text_print (obj_out, "\n", 1, NULL));
          check_nelem -= 10;
        }
    }
    }
  CHECK_PRINT_ERROR (text_print (obj_out, "}", 1, NULL));

exit_on_end:
  if (it != NULL)
    {
      set_iterator_free (it);
    }
  return error;

exit_on_error:

  CHECK_EXIT_ERROR (error);
  goto exit_on_end;
}


/*
 * process_value - dump one value in loader format
 *    return: NO_ERROR, if successful, error number, if not successful.
 *    value(in): the value to process
 */
static int
process_value (DB_VALUE * value, TEXT_OUTPUT * obj_out)
{
  int error = NO_ERROR;

  switch (DB_VALUE_TYPE (value))
    {
    case DB_TYPE_OID:
    case DB_TYPE_OBJECT:
      {
    OID *ref_oid;
    int ref_data;
    OID ref_class_oid;
    DB_OBJECT *classop;
    SM_CLASS *class_ptr;
    int *cls_no_ptr, cls_no;

    if (DB_VALUE_TYPE (value) == DB_TYPE_OID)
      {
        ref_oid = db_get_oid (value);
      }
    else
      {
        ref_oid = WS_OID (db_get_object (value));
      }

    if (required_class_only || (ref_oid == (OID *) 0) || (OID_EQ (ref_oid, &null_oid)) || datafile_per_class)
      {
        CHECK_PRINT_ERROR (text_print (obj_out, "NULL", 4, NULL));
        break;
      }

    OID_SET_NULL (&ref_class_oid);

    error = locator_does_exist (ref_oid, NULL_CHN, IS_LOCK, &ref_class_oid, NULL_CHN, false, false, NULL,
                    TM_TRAN_READ_FETCH_VERSION ());
    if (error == LC_EXIST)
      {
        classop = is_class (ref_oid, &ref_class_oid);
        if (classop != NULL)
          {
        ws_find (classop, (MOBJ *) (&class_ptr));
        if (class_ptr == NULL)
          {
            goto exit_on_error;
          }
        CHECK_PRINT_ERROR (text_print (obj_out, NULL, 0, "@%s", sm_ch_name ((MOBJ) class_ptr)));
        break;
          }

        /*
         * Lock referenced class with S_LOCK
         */
        error = NO_ERROR;   /* clear */
        classop = is_class (&ref_class_oid, WS_OID (sm_Root_class_mop));
        if (classop != NULL)
          {
        if (locator_fetch_class (classop, DB_FETCH_QUERY_READ) == NULL)
          {
            error = LC_ERROR;
          }
          }
        else
          {
        error = LC_ERROR;
          }
      }
    else if (error != LC_DOESNOT_EXIST)
      {
        error = LC_ERROR;
      }
    else
      {
        CHECK_PRINT_ERROR (text_print (obj_out, "NULL", 4, NULL));
        break;
      }

    if (error != NO_ERROR)
      {
        if (!ignore_err_flag)
          {
        goto exit_on_error;
          }
        else
          {
        (void) text_print (obj_out, "NULL", 4, NULL);
        break;
          }
      }

    /*
     * Output a reference indication if all classes are being processed,
     * or if a class_list is being used and references are being included,
     * or if a class_list is being used and the referenced class is a
     * requested class.  Otherwise, output "NULL".
     */

    /* figure out what it means for this to be NULL, I think this happens only for the reserved system classes like
     * db_user that are not dumped.  This is a problem because trigger objects for one, like to point directly at
     * the user object.  There will probably be others in time. */
    error = fh_get (cl_table, &ref_class_oid, (FH_DATA *) (&cls_no_ptr));
    if (error != NO_ERROR || cls_no_ptr == NULL)
      {
        CHECK_PRINT_ERROR (text_print (obj_out, "NULL", 4, NULL));
      }
    else
      {
        cls_no = *cls_no_ptr;
        if (!input_filename || include_references || (IS_CLASS_REQUESTED (cls_no)))
          {
        update_hash (ref_oid, &ref_class_oid, &ref_data);

        int *temp;
        error = fh_get (cl_table, &ref_class_oid, (FH_DATA *) (&temp));
        if (error != NO_ERROR || temp == NULL)
          {
            CHECK_PRINT_ERROR (text_print (obj_out, "NULL", 4, NULL));
          }
        else if (debug_flag)
          {
            CHECK_PRINT_ERROR (text_print
                       (obj_out, NULL, 0, "@%d|%d/*%d.%d.%d*/", *temp, ref_data, ref_oid->volid,
                    ref_oid->pageid, ref_oid->slotid));
          }
        else
          {
            CHECK_PRINT_ERROR (text_print (obj_out, NULL, 0, "@%d|%d", *temp, ref_data));
          }
          }
        else
          {
        CHECK_PRINT_ERROR (text_print (obj_out, "NULL", 4, NULL));
          }
      }
    break;
      }

    case DB_TYPE_SET:
    case DB_TYPE_MULTISET:
    case DB_TYPE_SEQUENCE:
      CHECK_PRINT_ERROR (process_set (db_get_set (value), obj_out));
      break;

    case DB_TYPE_BLOB:
    case DB_TYPE_CLOB:
      {
    DB_ELO *elo;
    DB_TYPE dt = db_value_type (value);
    char dts;

    if (dt == DB_TYPE_BLOB)
      {
        dts = 'B';
      }
    else
      {
        dts = 'C';
      }

    elo = db_get_elo (value);

    if (elo != NULL)
      {
        if (elo->type == ELO_FBO)
          {
        CHECK_PRINT_ERROR (text_print
                   (obj_out, NULL, 0, "^E'%c%lld|%s|%s'", dts, elo->size, elo->locator,
                    elo->meta_data != NULL ? elo->meta_data : ""));
          }
        else
          {
        /* should not happen */
        assert (0);
          }
      }
    else
      {
        CHECK_PRINT_ERROR (text_print (obj_out, "NULL", 4, NULL));
      }
    break;
      }

    default:
      CHECK_PRINT_ERROR (desc_value_special_fprint (obj_out, value));
      break;
    }

exit_on_end:

  return error;

exit_on_error:

  CHECK_EXIT_ERROR (error);
  goto exit_on_end;
}


/*
 * update_hash - update obj_table hash
 *    return: void
 *    object_oid(in): the object oid used as hash key
 *    class_oid(in): the oid of the object's class
 *    data(out): the value to associate with the oid
 * Note:
 *    If the object oid exists, return the data.  Otherwise, get the value for
 *    the class oid, increment it and use it for the data.  Store the data
 *    with the object oid  and class oid.  The data for the class id is the
 *    next number to use for the class.  If the class oid doesn't exist,
 *    initialize the data to 1.
 */
static void
update_hash (OID * object_oid, OID * class_oid, int *data)
{
  int *d;
  int error;

  error = fh_get (obj_table, object_oid, (FH_DATA *) (&d));
  if (error != NO_ERROR || d == NULL)
    {
      error = fh_get (obj_table, class_oid, (FH_DATA *) (&d));
      if (error != NO_ERROR || d == NULL)
    {
      *data = 1;
    }
      else
    {
      *data = *d + 1;
    }
      if (fh_put (obj_table, class_oid, data) != NO_ERROR)
    {
      perror ("SYSTEM ERROR related with hash-file\n==>unloaddb is NOT completed");
      _exit (1);
    }
      if (fh_put (obj_table, object_oid, data) != NO_ERROR)
    {
      perror ("SYSTEM ERROR related with hash-file\n==>unloaddb is NOT completed");
      _exit (1);
    }
    }
  else
    {
      *data = *d;
    }
}


/*
 * is_class - determine whether the object is actually a class.
 *    return: MOP for the object
 *    obj_oid(in): the object oid
 *    class_oid(in): the class oid
 */
static DB_OBJECT *
is_class (OID * obj_oid, OID * class_oid)
{
  if (OID_EQ (class_oid, WS_OID (sm_Root_class_mop)))
    {
      return ws_mop (obj_oid, NULL);
    }
  return 0;
}


/*
 * is_req_class - determine whether the class was requested in the input file
 *    return: 1 if true, otherwise 0.
 *    class(in): the class object
 */
int
is_req_class (DB_OBJECT * class_)
{
  int n;

  for (n = 0; n < class_table->num; ++n)
    {
      if (req_class_table[n] == class_)
    return 1;
    }
  return 0;
}


/*
 * all_classes_processed - compares class_requested with class_processed.
 *    return: 1 if true, otherwise 0.
 */
static int
all_classes_processed (void)
{
  int n;

  for (n = 0; n < (class_table->num + 7) / 8; ++n)
    {
      if ((class_requested[n] | class_referenced[n]) != class_processed[n])
    return 0;
    }
  return 1;
}

/*
 * get_requested_classes - read the class names from input_filename
 *    return: 0 for success, non-zero otherwise
 *    input_filename(in): the name of the input_file
 *    class_list(out): the class table to store classes in
 */
int
get_requested_classes (const char *input_filename, DB_OBJECT * class_list[])
{
  FILE *input_file;
  char buffer[LINE_MAX];
  int i = 0;            /* index of class_list */
  int error = NO_ERROR;

  if (input_filename == NULL)
    {
      return NO_ERROR;
    }

  input_file = fopen (input_filename, "r");
  if (input_file == NULL)
    {
      perror (input_filename);
      return ER_GENERIC_ERROR;
    }

  while (fgets ((char *) buffer, LINE_MAX, input_file) != NULL)
    {
      DB_OBJECT *class_ = NULL;
      MOP *sub_partitions = NULL;
      int is_partition = 0;
      int j = 0;        /* index of sub_partitions */
      const char *dot = NULL;
      char downcase_class_name[DB_MAX_IDENTIFIER_LENGTH];
      int len = 0;

      trim (buffer);

      len = STATIC_CAST (int, strlen (buffer));
      if (len < 1)
    {
      /* empty string */
      continue;
    }

      dot = strchr (buffer, '.');
      if (dot)
    {
      /* user specified name */

      /* user name of user specified name */
      len = STATIC_CAST (int, dot - buffer);
      if (len >= DB_MAX_USER_LENGTH)
        {
          PRINT_AND_LOG_ERR_MSG (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_UNLOADDB,
                             UNLOADDB_MSG_EXCEED_MAX_USER_LEN), DB_MAX_USER_LENGTH - 1);
          fclose (input_file);
          return ER_GENERIC_ERROR;
        }

      /* class name of user specified name */
      len = STATIC_CAST (int, strlen (dot + 1));
    }

      if (len >= DB_MAX_IDENTIFIER_LENGTH - DB_MAX_USER_LENGTH)
    {
      PRINT_AND_LOG_ERR_MSG (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_UNLOADDB,
                         UNLOADDB_MSG_EXCEED_MAX_LEN),
                 DB_MAX_IDENTIFIER_LENGTH - DB_MAX_USER_LENGTH - 1);
      fclose (input_file);
      return ER_GENERIC_ERROR;
    }

      sm_user_specified_name (buffer, downcase_class_name, DB_MAX_IDENTIFIER_LENGTH);

      class_ = locator_find_class (downcase_class_name);
      if (class_ != NULL)
    {
      class_list[i] = class_;
      error = sm_partitioned_class_type (class_, &is_partition, NULL, &sub_partitions);
      if (error != NO_ERROR)
        {
          fclose (input_file);
          return error;
        }
      if (is_partition == 1 && sub_partitions != NULL)
        {
          for (j = 0; sub_partitions[j]; j++)
        {
          i++;
          class_list[i] = sub_partitions[j];
        }
        }
      if (sub_partitions != NULL)
        {
          free_and_init (sub_partitions);
        }
      i++;
    }
      else
    {
      PRINT_AND_LOG_ERR_MSG (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_UNLOADDB,
                         UNLOADDB_MSG_CLASS_NOT_FOUND), downcase_class_name);
    }
    }               /* while */

  fclose (input_file);

  return error;
}

static int
create_filename (const char *output_dirname, const char *output_prefix, const char *suffix, char *output_filename_p,
         const size_t filename_size)
{
  if (output_dirname == NULL)
    {
      output_dirname = ".";
    }

  size_t total = strlen (output_dirname) + strlen (output_prefix) + strlen (suffix) + 8;

#if defined(MULTI_PROCESSING_UNLOADDB_WITH_FORK)
  if (g_parallel_process_cnt > 1)
    {
      char proc_name[32];

      total += sprintf (proc_name, "p%d-%d", g_parallel_process_cnt, g_parallel_process_idx);
      if (total > filename_size)
    {
      return -1;
    }
      snprintf (output_filename_p, filename_size - 1, "%s/%s_%s%s", output_dirname, output_prefix, proc_name, suffix);
    }
  else
#endif
    {
      if (total > filename_size)
    {
      return -1;
    }

      snprintf (output_filename_p, filename_size - 1, "%s/%s%s", output_dirname, output_prefix, suffix);
    }

  return 0;
}

static void
close_object_file ()
{

  if (g_fd_handle != INVALID_FILE_NO)
    {
      close (g_fd_handle);
      g_fd_handle = INVALID_FILE_NO;
    }
}

static bool
open_object_file (extract_context & ctxt, const char *output_dirname, const char *class_name)
{
  char out_fname[PATH_MAX];
  char tmp_name[DB_MAX_IDENTIFIER_LENGTH * 2];
  char *name_ptr = (char *) class_name;

#if defined(MULTI_PROCESSING_UNLOADDB_WITH_FORK)
  if (g_parallel_process_cnt > 1)
    {
      assert (class_name && *class_name);
      sprintf (tmp_name, "p%d-%d_%s", g_parallel_process_cnt, g_parallel_process_idx, class_name);
      name_ptr = tmp_name;
    }
#endif

  out_fname[0] = '\0';
  if (class_name && *class_name)
    {
      snprintf (out_fname, PATH_MAX - 1, "%s/%s_%s%s", output_dirname, ctxt.output_prefix, name_ptr, OBJECT_SUFFIX);
    }
  else
    {
      snprintf (out_fname, PATH_MAX - 1, "%s/%s%s", output_dirname, ctxt.output_prefix, OBJECT_SUFFIX);
    }

  g_fd_handle = open (out_fname, O_WRONLY | O_CREAT | O_TRUNC, CREAT_OBJECT_FILE_PERM);
  if (g_fd_handle == INVALID_FILE_NO)
    {
      fprintf (stderr, "%s: %s.\n\n", ctxt.exec_name, strerror (errno));
      return false;
    }

  return true;
}

//#define USE_CFG_FILE_TUNNING_TEST
#if defined(USE_CFG_FILE_TUNNING_TEST)
void
read_unload_cfg (int thr_max, int *io_buffer_size, int *fetched_list_size, int *writer_q_size)
{
  FILE *fp;
  char buf[1024];
  char *pv;
  int buffer_size, list_weight, q_weight;

  fp = fopen ("unloaddb.cfg", "rt");
  if (!fp)
    {
      return;
    }

  // io_buffer_size
  // list_weight
  // queue_weight

  buffer_size = list_weight = q_weight = -1;

  while (!feof (fp))
    {
      buf[0] = 0x00;
      fgets (buf, sizeof (buf), fp);

      if (buf[0] == '#')
    {
      continue;
    }

      trim (buf);

      pv = strrchr (buf, '=');
      if (pv == NULL)
    {
      continue;
    }

      *pv = 0x00;
      pv++;

      if (strcasecmp (buf, "io_buffer_size") == 0)
    {
      buffer_size = atoi (pv);
    }
      else if (strcasecmp (buf, "list_weight") == 0)
    {
      list_weight = atoi (pv);
    }
      else if (strcasecmp (buf, "queue_weight") == 0)
    {
      q_weight = atoi (pv);
    }
    }
  fclose (fp);

  if (buffer_size > 0)
    {
      *io_buffer_size = buffer_size;
    }
  if (list_weight > 0)
    {
      *fetched_list_size = (thr_max * list_weight) + 1;
    }
  if (q_weight > 0)
    {
      *writer_q_size = (thr_max * q_weight) + 1;
    }
}
#endif

static bool
init_thread_param (const char *output_dirname, int nthreads)
{
  int i;
  int thr_max = (nthreads > 0) ? nthreads : 1;

  g_thr_param = (UNLD_THR_PARAM *) calloc (thr_max, sizeof (UNLD_THR_PARAM));
  if (g_thr_param == NULL)
    {
      assert (false);
      fprintf (stderr, "Failed to allocate memory. (%ld bytes)\n\n", ((size_t) thr_max * sizeof (UNLD_THR_PARAM)));
      return false;
    }

  for (i = 0; i < thr_max; i++)
    {
      g_thr_param[i].text_output.ref_thread_param_idx = i;
    }

  /* There may be content that needs to be output even before calling process_class().
   * Let's output this area to a file immediately.
   */
  g_multi_thread_mode = false;

  int _blksize = 4096;
  g_io_buffer_size = 1024 * 1024;   /* 1 Mbyte */
#if !defined (WINDOWS)
  struct stat stbuf;
  if (stat (output_dirname, &stbuf) != -1)
    {
      _blksize = stbuf.st_blksize;
    }
#endif /* WINDOWS */

  int writer_q_size = 0;

  writer_q_size = (thr_max * 4) + 1;
  max_fetched_copyarea_list = (thr_max * 2) + 1;

#if defined(USE_CFG_FILE_TUNNING_TEST)
  read_unload_cfg (thr_max, &g_io_buffer_size, &max_fetched_copyarea_list, &writer_q_size);
#endif
  /*
   * Determine the IO buffer size by specifying a multiple of the
   * natural block size for the device.
   * NEED FUTURE OPTIMIZATION
   */
  g_io_buffer_size -= (g_io_buffer_size % _blksize);
  if (writer_q_size < 17)
    {
      writer_q_size = 17;
    }

#if defined(USE_CFG_FILE_TUNNING_TEST)
  fprintf (stderr, "Notice: read unloaddb.cfg, io_buffer_size=%d fetch list=%d wq_size=%d\n",
       g_io_buffer_size, max_fetched_copyarea_list, writer_q_size);
#endif

  return init_queue_n_list_for_object_file (writer_q_size, (writer_q_size * 2));
}

static void
quit_thread_param ()
{
  if (g_thr_param)
    {
      free_and_init (g_thr_param);
    }
}

static void
print_monitoring_info (const char *class_name, int nthreads)
{
#if !defined(WINDOWS)
  FILE *fp = stdout;

  assert (g_sampling_records >= 0);

  if (verbose_flag == false)
    {
      int64_t class_objects = class_objects_atomic;
      fprintf (fp, ALIGN_SPACE_FMT "Records: %ld\n", class_name, class_objects);
    }

  fprintf (fp, ALIGN_SPACE_FMT "Elapsed: %12.6f sec\n", "",
       wi_unload_class.ts_wait_sum.tv_sec + ((double) wi_unload_class.ts_wait_sum.tv_nsec / NANO_PREC_VAL));
  fprintf (fp, ALIGN_SPACE_FMT "Fetch  : %12.6f sec, count= %" PRId64 "\n", "",
       g_uci->wi_fetch.ts_wait_sum.tv_sec + ((double) g_uci->wi_fetch.ts_wait_sum.tv_nsec / NANO_PREC_VAL),
       g_uci->wi_fetch.cnt);

  fprintf (fp, ALIGN_SPACE_FMT "Write  : %12.6f sec\n", "",
       wi_write_file.ts_wait_sum.tv_sec + ((double) wi_write_file.ts_wait_sum.tv_nsec / NANO_PREC_VAL));

  if (nthreads <= 0)
    {
      return;
    }

  fprintf (fp, ALIGN_SPACE_FMT "Add L  : %12.6f sec, count= %" PRId64 "\n", "",
       g_uci->cparea_lst_ref->m_wi_add_list.ts_wait_sum.tv_sec +
       ((double) g_uci->cparea_lst_ref->m_wi_add_list.ts_wait_sum.tv_nsec / NANO_PREC_VAL),
       g_uci->cparea_lst_ref->m_wi_add_list.cnt);


  S_WAITING_INFO winfo;

  memset (&winfo, 0x00, sizeof (S_WAITING_INFO));
  for (int i = 0; i < nthreads; i++)
    {
#if 0
      fprintf (fp, ALIGN_SPACE_FMT "Get L  : %12.6f sec, count= %" PRId64 "\n", "",
           g_thr_param[i].wi_get_list.ts_wait_sum.tv_sec +
           ((double) g_thr_param[i].wi_get_list.ts_wait_sum.tv_nsec / NANO_PREC_VAL),
           g_thr_param[i].wi_get_list.cnt);
#endif
      timespec_addup (&(winfo.ts_wait_sum), &(g_thr_param[i].wi_get_list.ts_wait_sum));
      winfo.cnt += g_thr_param[i].wi_get_list.cnt;
    }

  fprintf (fp, ALIGN_SPACE_FMT "Get L  : %12.6f sec, count= %" PRId64 "\n", "",
       (winfo.ts_wait_sum.tv_sec + ((double) winfo.ts_wait_sum.tv_nsec / NANO_PREC_VAL)) / nthreads,
       (int64_t) (winfo.cnt / nthreads));


  memset (&winfo, 0x00, sizeof (S_WAITING_INFO));
  for (int i = 0; i < nthreads; i++)
    {
      timespec_addup (&(winfo.ts_wait_sum), &(g_thr_param[i].wi_add_Q.ts_wait_sum));
      winfo.cnt += g_thr_param[i].wi_add_Q.cnt;
    }

  fprintf (fp, ALIGN_SPACE_FMT "Add Q  : %12.6f sec, count= %" PRId64 "\n", "",
       (winfo.ts_wait_sum.tv_sec + ((double) winfo.ts_wait_sum.tv_nsec / NANO_PREC_VAL)) / nthreads,
       (int64_t) (winfo.cnt / nthreads));

  fprintf (fp, ALIGN_SPACE_FMT "Get Q  : %12.6f sec, count= %" PRId64 "\n", "",
       (wi_w_blk_getQ.ts_wait_sum.tv_sec + ((double) wi_w_blk_getQ.ts_wait_sum.tv_nsec / NANO_PREC_VAL)),
       (int64_t) (wi_w_blk_getQ.cnt));

  memset (&winfo, 0x00, sizeof (S_WAITING_INFO));
  for (int i = 0; i < nthreads; i++)
    {
      timespec_addup (&(winfo.ts_wait_sum), &(g_thr_param[i].wi_to_obj_str[0].ts_wait_sum));
      winfo.cnt += g_thr_param[i].wi_to_obj_str[0].cnt;
    }
  fprintf (fp, ALIGN_SPACE_FMT "to obj : %12.6f sec\n", "",
       (winfo.ts_wait_sum.tv_sec + ((double) winfo.ts_wait_sum.tv_nsec / NANO_PREC_VAL)) / nthreads);

  memset (&winfo, 0x00, sizeof (S_WAITING_INFO));
  for (int i = 0; i < nthreads; i++)
    {
      timespec_addup (&(winfo.ts_wait_sum), &(g_thr_param[i].wi_to_obj_str[1].ts_wait_sum));
      winfo.cnt += g_thr_param[i].wi_to_obj_str[1].cnt;
    }
  fprintf (fp, ALIGN_SPACE_FMT "to str : %12.6f sec\n", "",
       (winfo.ts_wait_sum.tv_sec + ((double) winfo.ts_wait_sum.tv_nsec / NANO_PREC_VAL)) / nthreads);
#endif
}

#if defined(MULTI_PROCESSING_UNLOADDB_WITH_FORK)
static void
unload_log_write (char *log_file_name, const char *fmt, ...)
{
#define MAX_RETRY_COUNT 10
  int retry_count = 0;
  FILE *fp;

  if (log_file_name == NULL || *log_file_name == '\0')
    return;

retry:
  fp = fopen (log_file_name, "a+");
  if (fp != NULL)
    {
      if (lockf (fileno (fp), F_TLOCK, 0) < 0)
    {
      fclose (fp);

      if (retry_count < MAX_RETRY_COUNT)
        {
          SLEEP_MILISEC (0, 100);
          retry_count++;
          goto retry;
        }

      return;
    }
    }

  if (fp)
    {
      va_list ap;

      va_start (ap, fmt);
      int result = vfprintf (fp, fmt, ap);
      va_end (ap);

      fclose (fp);
    }
}
#endif