Skip to content

File compactdb_cl.c

File List > cubrid > src > executables > compactdb_cl.c

Go to the documentation of this file

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


#include "config.h"

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

#include "porting.h"
#include "dbtype.h"
#include "load_object.h"
#include "db.h"
#include "locator_cl.h"
#include "schema_manager.h"
#include "object_accessor.h"
#include "set_object.h"
#include "message_catalog.h"
#include "network_interface_cl.h"
#include "server_interface.h"
#include "system_parameter.h"
#include "utility.h"
#include "authenticate.h"
#include "transaction_cl.h"
#include "execute_schema.h"
#include "object_primitive.h"

#define COMPACT_MIN_PAGES 1
#define COMPACT_MAX_PAGES 20

#define COMPACT_INSTANCE_MIN_LOCK_TIMEOUT 1
#define COMPACT_INSTANCE_MAX_LOCK_TIMEOUT 10

#define COMPACT_CLASS_MIN_LOCK_TIMEOUT 1
#define COMPACT_CLASS_MAX_LOCK_TIMEOUT 10

static int is_not_system_class (MOBJ class_);
static int do_reclaim_addresses (OID * const *class_oids, const int num_class_oids,
                 int *const num_classes_fully_processed, const bool verbose,
                 const int class_lock_timeout);
static int do_reclaim_class_addresses (const OID class_oid, char **clas_name, bool * const any_class_can_be_referenced,
                       bool * const correctly_processed, bool * const addresses_reclaimed,
                       int *const error_while_processing);
static int class_instances_can_be_referenced (MOP mop, MOP parent_mop, bool * const class_can_be_referenced,
                          bool * const any_class_can_be_referenced, MOP * const all_mops,
                          const int num_mops);
static int class_referenced_by_class (MOP referenced_mop, MOP parent_mop, MOP referring_mop,
                      bool * const class_can_be_referenced, bool * const any_class_can_be_referenced);
static int class_referenced_by_attributes (MOP referenced_class, MOP parent_mop, SM_ATTRIBUTE * const attributes_list,
                       bool * const class_can_be_referenced,
                       bool * const any_class_can_be_referenced);
static void class_referenced_by_domain (MOP referenced_class, TP_DOMAIN * const domain,
                    bool * const class_can_be_referenced, bool * const any_class_can_be_referenced);
extern int get_class_mops (char **class_names, int num_class, MOP ** class_list, int *num_class_list);
extern int get_class_mops_from_file (const char *input_filename, MOP ** class_list, int *num_class_mops);


/*
 * compact_usage() - print an usage of the compactdb-utility
 *   return: void
 *   exec_name(in): a name of this application
 */
static void
compactdb_usage (const char *argv0)
{
  const char *exec_name;

  exec_name = basename ((char *) argv0);
  util_log_write_errid (MSGCAT_UTIL_GENERIC_INVALID_ARGUMENT);
  printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, 60), exec_name);
}

/*
 * find_oid - search an oid in a list of oids
 *    return: the index of the oid on success, -1 otherwise
 *    oid(in): the searched oid
 *    oids_list(in): the list where oid is searched
 *    num_class_mops(in): the length of oids_list
 */
static int
find_oid (OID * oid, OID ** oids_list, int num_oids)
{
  int i;
  if (oid == NULL || oids_list == NULL || num_oids <= 0)
    {
      return -1;
    }

  for (i = 0; i < num_oids; i++)
    {
      if (oids_list[i] == NULL)
    {
      continue;
    }

      if (OID_EQ (oid, oids_list[i]))
    {
      return i;
    }
    }

  return -1;
}

/*
 * show_statistics - show the statistics for specified class oids
 *    return:
 *    class_oid(in) : class oid
 *    unlocked_class(in) : true if the class was not locked
 *    valid_class(in): true if the class was valid
 *    processed_class(in): true if the class was processed
 *    total_objects(in): total class objects
 *    failed_objects(in): failed class objects
 *    modified_objects(in): modified class objects
 *    big_objects(in): big class objects
 *    delete_old_repr_flag: delete old representation flag
 *    old_repr_deleted(in): old class representations removed from catalog
 */
static void
show_statistics (OID * class_oid, bool unlocked_class, bool valid_class, bool processed_class, int total_objects,
         int failed_objects, int modified_objects, int big_objects, bool delete_old_repr_flag,
         bool old_repr_deleted)
{
  MOP class_mop = NULL;
  char *temp_class_name;

  class_mop = db_object (class_oid);
  if (class_mop == NULL)
    {
      printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_INVALID_CLASS));
      return;
    }

  temp_class_name = (char *) db_get_class_name (class_mop);
  if (temp_class_name == NULL || strlen (temp_class_name) == 0)
    {
      printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_UNKNOWN_CLASS_NAME));
    }
  else
    {
      printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_CLASS), temp_class_name);
    }

  if (!valid_class)
    {
      printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_INVALID_CLASS));

      return;
    }

  if (!processed_class)
    {
      printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_PROCESS_CLASS_ERROR));

    }

  if (!unlocked_class)
    {
      printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_LOCKED_CLASS));
    }

  printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_TOTAL_OBJECTS), total_objects);

  printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_FAILED_OBJECTS),
      failed_objects);

  printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_MODIFIED_OBJECTS),
      modified_objects);

  printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_BIG_OBJECTS), big_objects);

  if (delete_old_repr_flag)
    {
      if (old_repr_deleted)
    {
      printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_REPR_DELETED));
    }
      else
    {
      printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_REPR_CANT_DELETE));
    }
    }
}

/*
 * get_name_from_class_oid - get the name of the class from class oid
 *    return:  the name of the class
 *    class_oid(in) : class oid
 */
static char *
get_name_from_class_oid (OID * class_oid)
{
  MOP class_mop = NULL;
  char *temp_class_name;
  char *result;

  if (!class_oid)
    {
      return NULL;
    }

  class_mop = db_object (class_oid);
  if (class_mop == NULL)
    {
      return NULL;
    }

  temp_class_name = (char *) db_get_class_name (class_mop);
  if (temp_class_name == NULL)
    {
      return NULL;
    }

  result = (char *) malloc (sizeof (char) * (strlen (temp_class_name) + 1));
  if (result == NULL)
    {
      return NULL;
    }

  strcpy (result, temp_class_name);

  return result;
}

/*
 * compactdb_start - Compact classes
 *    return: error code
 *    verbose_flag(in):
 *    delete_old_repr_flag(in): delete old class representations from catalog
 *    input_filename(in): classes file name
 *    input_class_names(in): classes list
 *    input_class_length(in): classes list length
 *    max_processed_space(in): maximum space to process for one iteration
 */
static int
compactdb_start (bool verbose_flag, bool delete_old_repr_flag, char *input_filename, char **input_class_names,
         int input_class_length, int max_processed_space, int instance_lock_timeout, int class_lock_timeout,
         DB_TRAN_ISOLATION tran_isolation)
{
  int status = NO_ERROR;
  OID **class_oids = NULL;
  int i, num_classes = 0;
  LIST_MOPS *class_table = NULL;
  OID last_processed_class_oid, last_processed_oid;
  int *total_objects = NULL, *iteration_total_objects = NULL;
  int *failed_objects = NULL, *iteration_failed_objects = NULL;
  int *modified_objects = NULL, *iteration_modified_objects = NULL;
  char *incomplete_processing = NULL;
  int *big_objects = NULL, *iteration_big_objects = NULL;
  int *initial_last_repr = NULL;
  MOP *class_mops = NULL;
  int last_completed_class_index, temp_index;
  int num_class_mops = 0;
  SM_CLASS *class_ptr = NULL;
  int num_classes_fully_compacted = 0;
  char *class_name = NULL;
  MOP *processed_class_mops = NULL;

  if (input_filename && input_class_names && input_class_length > 0)
    {
      return ER_FAILED;
    }

  status = compact_db_start ();
  if (status != NO_ERROR)
    {
      if (status == ER_COMPACTDB_ALREADY_STARTED)
    {
      printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_ALREADY_STARTED));
    }

      return ER_FAILED;
    }

  tran_reset_wait_times (class_lock_timeout);

  if (input_class_names && input_class_length > 0)
    {
      status = get_class_mops (input_class_names, input_class_length, &class_mops, &num_class_mops);
      if (status != NO_ERROR)
    {
      goto error;
    }
    }
  else if (input_filename)
    {
      status = get_class_mops_from_file (input_filename, &class_mops, &num_class_mops);
      if (status != NO_ERROR)
    {
      goto error;
    }
    }
  else
    {
      class_table = locator_get_all_mops (sm_Root_class_mop, DB_FETCH_QUERY_READ, NULL);
      if (!class_table)
    {
      status = ER_FAILED;
      goto error;
    }

      class_mops = class_table->mops;
      num_class_mops = class_table->num;
    }

  class_oids = (OID **) malloc (DB_SIZEOF (OID *) * (num_class_mops));
  if (class_oids == NULL)
    {
      status = ER_FAILED;
      goto error;
    }

  for (i = 0; i < num_class_mops; i++)
    {
      class_oids[i] = NULL;
    }

  processed_class_mops = (DB_OBJECT **) malloc (DB_SIZEOF (DB_OBJECT *) * (num_class_mops));
  if (processed_class_mops == NULL)
    {
      status = ER_FAILED;
      goto error;
    }

  for (i = 0; i < num_class_mops; i++)
    {
      processed_class_mops[i] = NULL;
    }

  num_classes = 0;
  for (i = 0; i < num_class_mops; i++)
    {
      ws_find (class_mops[i], (MOBJ *) (&class_ptr));
      if (class_ptr == NULL)
    {
      continue;
    }

      if (class_ptr->class_type != SM_CLASS_CT)
    {
      continue;
    }

      class_oids[num_classes] = ws_oid (class_mops[i]);
      if (class_oids[num_classes] != NULL)
    {
      processed_class_mops[num_classes] = class_mops[i];
      num_classes++;
    }
    }

  if (num_classes == 0)
    {
      printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_NOTHING_TO_PROCESS));
      goto error;
    }

  total_objects = (int *) malloc (num_classes * sizeof (int));
  if (total_objects == NULL)
    {
      status = ER_FAILED;
      goto error;
    }

  iteration_total_objects = (int *) malloc (num_classes * sizeof (int));
  if (iteration_total_objects == NULL)
    {
      status = ER_FAILED;
      goto error;
    }

  failed_objects = (int *) malloc (num_classes * sizeof (int));
  if (failed_objects == NULL)
    {
      status = ER_FAILED;
      goto error;
    }

  iteration_failed_objects = (int *) malloc (num_classes * sizeof (int));
  if (iteration_failed_objects == NULL)
    {
      status = ER_FAILED;
      goto error;
    }

  modified_objects = (int *) malloc (num_classes * sizeof (int));
  if (modified_objects == NULL)
    {
      status = ER_FAILED;
      goto error;
    }

  iteration_modified_objects = (int *) malloc (num_classes * sizeof (int));
  if (iteration_modified_objects == NULL)
    {
      status = ER_FAILED;
      goto error;
    }

  big_objects = (int *) malloc (num_classes * sizeof (int));
  if (big_objects == NULL)
    {
      status = ER_FAILED;
      goto error;
    }

  iteration_big_objects = (int *) malloc (num_classes * sizeof (int));
  if (iteration_big_objects == NULL)
    {
      status = ER_FAILED;
      goto error;
    }

  initial_last_repr = (int *) malloc (num_classes * sizeof (int));
  if (initial_last_repr == NULL)
    {
      status = ER_FAILED;
      goto error;
    }

  incomplete_processing = (char *) malloc (num_classes * sizeof (char));
  if (incomplete_processing == NULL)
    {
      status = ER_FAILED;
      goto error;
    }

  for (i = 0; i < num_classes; i++)
    {
      total_objects[i] = 0;
      failed_objects[i] = 0;
      modified_objects[i] = 0;
      big_objects[i] = 0;
      incomplete_processing[i] = 0;
      initial_last_repr[i] = NULL_REPRID;
    }

  for (i = 0; i < num_class_mops; i++)
    {
      status = locator_flush_all_instances (class_mops[i], DECACHE);
      if (status != NO_ERROR)
    {
      goto error;
    }
    }

  status = db_commit_transaction ();
  if (status != NO_ERROR)
    {
      goto error;
    }

  COPY_OID (&last_processed_class_oid, class_oids[0]);
  OID_SET_NULL (&last_processed_oid);
  temp_index = -1;
  last_completed_class_index = -1;

  if (verbose_flag)
    {
      printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_PASS1));
    }

  while (true)
    {
      status = db_set_isolation (tran_isolation);
      if (status != NO_ERROR)
    {
      if (verbose_flag)
        {
          printf (msgcat_message
              (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_ISOLATION_LEVEL_FAILURE));
        }

      status = ER_FAILED;
      goto error;
    }

      status =
    boot_compact_classes (class_oids, num_classes, max_processed_space, instance_lock_timeout, class_lock_timeout,
                  delete_old_repr_flag, &last_processed_class_oid, &last_processed_oid,
                  iteration_total_objects, iteration_failed_objects, iteration_modified_objects,
                  iteration_big_objects, initial_last_repr);

      if (OID_ISNULL (&last_processed_class_oid))
    {
      temp_index = num_classes;
    }
      else
    {
      temp_index = find_oid (&last_processed_class_oid, class_oids, num_classes);
    }

      switch (status)
    {
    case NO_ERROR:
      if (delete_old_repr_flag && temp_index - 1 > last_completed_class_index)
        {
          for (i = last_completed_class_index + 1; i < temp_index; i++)
        {
          if (initial_last_repr[i] == COMPACTDB_REPR_DELETED)
            {
              sm_destroy_representations (processed_class_mops[i]);
            }
        }
        }

      status = db_commit_transaction ();
      if (status != NO_ERROR)
        {
          goto error;
        }
      break;

    case ER_LK_UNILATERALLY_ABORTED:
      status = tran_abort_only_client (false);
      if (status != NO_ERROR)
        {
          goto error;
        }
      break;

    case ER_FAILED:
      status = db_abort_transaction ();
      if (status != NO_ERROR)
        {
          goto error;
        }
      break;

    default:
      db_abort_transaction ();
      status = ER_FAILED;
      goto error;
    }

      for (i = 0; i < num_classes; i++)
    {
      if (iteration_total_objects[i] >= 0)
        {
          total_objects[i] += iteration_total_objects[i];
          failed_objects[i] += iteration_failed_objects[i];
          modified_objects[i] += iteration_modified_objects[i];
          big_objects[i] += iteration_big_objects[i];
        }
      else
        {
          incomplete_processing[i] = iteration_total_objects[i];
        }
    }

      if (temp_index - 1 > last_completed_class_index)
    {
      for (i = last_completed_class_index + 1; i < temp_index; i++)
        {
          status = db_set_isolation (tran_isolation);
          if (status != NO_ERROR)
        {
          if (verbose_flag)
            {
              printf (msgcat_message
                  (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_ISOLATION_LEVEL_FAILURE));
            }

          status = ER_FAILED;
          goto error;
        }

          tran_reset_wait_times (class_lock_timeout);

          show_statistics (class_oids[i], incomplete_processing[i] != COMPACTDB_LOCKED_CLASS,
                   incomplete_processing[i] != COMPACTDB_INVALID_CLASS,
                   incomplete_processing[i] != COMPACTDB_UNPROCESSED_CLASS, total_objects[i],
                   failed_objects[i], modified_objects[i], big_objects[i], delete_old_repr_flag,
                   initial_last_repr[i] == COMPACTDB_REPR_DELETED);

          db_commit_transaction ();
        }

      last_completed_class_index = temp_index - 1;
    }

      if (OID_ISNULL (&last_processed_class_oid))
    {
      break;
    }
    }

  if (verbose_flag)
    {
      printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_PASS2));
    }
  status = do_reclaim_addresses (class_oids, num_classes, &num_classes_fully_compacted, verbose_flag,
                 class_lock_timeout);
  if (status != NO_ERROR)
    {
      goto error;
    }

  if (verbose_flag)
    {
      printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_PASS3));
    }

  for (i = 0; i < num_classes; i++)
    {
      status = db_set_isolation (tran_isolation);
      if (status != NO_ERROR)
    {
      if (verbose_flag)
        {
          printf (msgcat_message
              (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_ISOLATION_LEVEL_FAILURE));
        }

      status = ER_FAILED;
      goto error;
    }

      tran_reset_wait_times (class_lock_timeout);

      status = boot_heap_compact (class_oids[i]);
      switch (status)
    {
    case NO_ERROR:
      status = db_commit_transaction ();
      if (status != NO_ERROR)
        {
          goto error;
        }
      break;

    case ER_LK_UNILATERALLY_ABORTED:
      status = tran_abort_only_client (false);
      if (status != NO_ERROR)
        {
          goto error;
        }
      break;

    default:
      status = db_abort_transaction ();
      if (status != NO_ERROR)
        {
          goto error;
        }
      break;
    }

      class_name = get_name_from_class_oid (class_oids[i]);
      if (class_name == NULL)
    {
      printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_UNKNOWN_CLASS_NAME));
    }
      else
    {
      printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_CLASS), class_name);
    }

      if (status != NO_ERROR)
    {
      printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_HEAP_COMPACT_FAILED));
    }
      else
    {
      printf (msgcat_message
          (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_HEAP_COMPACT_SUCCEEDED));
    }

      if (class_name)
    {
      free (class_name);
      class_name = NULL;
    }

      db_commit_transaction ();
    }

error:
  if (class_oids)
    {
      free_and_init (class_oids);
    }

  if (processed_class_mops)
    {
      free_and_init (processed_class_mops);
    }

  if (total_objects)
    {
      free_and_init (total_objects);
    }

  if (iteration_total_objects)
    {
      free_and_init (iteration_total_objects);
    }

  if (failed_objects)
    {
      free_and_init (failed_objects);
    }

  if (iteration_failed_objects)
    {
      free_and_init (iteration_failed_objects);
    }

  if (modified_objects)
    {
      free_and_init (modified_objects);
    }

  if (iteration_modified_objects)
    {
      free_and_init (iteration_modified_objects);
    }

  if (big_objects)
    {
      free_and_init (big_objects);
    }

  if (iteration_big_objects)
    {
      free_and_init (iteration_big_objects);
    }

  if (initial_last_repr)
    {
      free_and_init (initial_last_repr);
    }

  if (incomplete_processing)
    {
      free_and_init (incomplete_processing);
    }

  if (class_table)
    {
      locator_free_list_mops (class_table);
    }
  else
    {
      if (class_mops)
    {
      for (i = 0; i < num_class_mops; i++)
        {
          class_mops[i] = NULL;
        }

      free_and_init (class_mops);
    }
    }

  compact_db_stop ();

  return status;
}

/*
 * compactdb - compactdb main routine
 *    return: 0 if successful, error code otherwise
 *    arg(in): a map of command line arguments
 */
int
compactdb (UTIL_FUNCTION_ARG * arg)
{
  UTIL_ARG_MAP *arg_map = arg->arg_map;
  const char *exec_name = arg->command_name;
  int error;
  int i, status = 0;
  const char *database_name;
  bool verbose_flag = 0, delete_old_repr_flag = 0, standby_compactdb_flag = 0;
  char *input_filename = NULL;
  int maximum_processed_space = 10 * DB_PAGESIZE, pages;
  int instance_lock_timeout, class_lock_timeout;
  char **tables = NULL;
  int table_size = 0;

  database_name = utility_get_option_string_value (arg_map, OPTION_STRING_TABLE, 0);
  verbose_flag = utility_get_option_bool_value (arg_map, COMPACT_VERBOSE_S);
  standby_compactdb_flag = utility_get_option_bool_value (arg_map, COMPACT_STANDBY_CS_MODE_S);
  input_filename = utility_get_option_string_value (arg_map, COMPACT_INPUT_CLASS_FILE_S, 0);

  pages = utility_get_option_int_value (arg_map, COMPACT_PAGES_COMMITED_ONCE_S);
  if (pages < COMPACT_MIN_PAGES || pages > COMPACT_MAX_PAGES)
    {
      fprintf (stderr, msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_FAILURE));
      PRINT_AND_LOG_ERR_MSG (msgcat_message
                 (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_OUT_OF_RANGE_PAGES),
                 COMPACT_MIN_PAGES, COMPACT_MAX_PAGES);

      return ER_GENERIC_ERROR;
    }

  if (database_name == NULL || database_name[0] == '\0' || utility_get_option_string_table_size (arg_map) < 1)
    {
      compactdb_usage (arg->argv0);
      return ER_GENERIC_ERROR;
    }

  table_size = utility_get_option_string_table_size (arg_map);
  if (table_size > 1 && input_filename != NULL)
    {
      compactdb_usage (arg->argv0);
      return ER_GENERIC_ERROR;
    }

  instance_lock_timeout = utility_get_option_int_value (arg_map, COMPACT_INSTANCE_LOCK_TIMEOUT_S);
  if (instance_lock_timeout < COMPACT_INSTANCE_MIN_LOCK_TIMEOUT
      || instance_lock_timeout > COMPACT_INSTANCE_MAX_LOCK_TIMEOUT)
    {
      fprintf (stderr, msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_FAILURE));

      PRINT_AND_LOG_ERR_MSG (msgcat_message
                 (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB,
                  COMPACTDB_MSG_OUT_OF_RANGE_INSTANCE_LOCK_TIMEOUT), COMPACT_INSTANCE_MIN_LOCK_TIMEOUT,
                 COMPACT_INSTANCE_MAX_LOCK_TIMEOUT);

      return ER_GENERIC_ERROR;
    }

  class_lock_timeout = utility_get_option_int_value (arg_map, COMPACT_CLASS_LOCK_TIMEOUT_S);
  if (class_lock_timeout < COMPACT_CLASS_MIN_LOCK_TIMEOUT || class_lock_timeout > COMPACT_CLASS_MAX_LOCK_TIMEOUT)
    {
      fprintf (stderr, msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_FAILURE));

      PRINT_AND_LOG_ERR_MSG (msgcat_message
                 (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB,
                  COMPACTDB_MSG_OUT_OF_RANGE_CLASS_LOCK_TIMEOUT), COMPACT_CLASS_MIN_LOCK_TIMEOUT,
                 COMPACT_CLASS_MAX_LOCK_TIMEOUT);

      return ER_GENERIC_ERROR;
    }

  delete_old_repr_flag = utility_get_option_bool_value (arg_map, COMPACT_DELETE_OLD_REPR_S);

  maximum_processed_space = pages * DB_PAGESIZE;

  if (table_size > 1)
    {
      tables = (char **) malloc (sizeof (char *) * (table_size - 1));
      if (tables == NULL)
    {
      PRINT_AND_LOG_ERR_MSG (msgcat_message
                 (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_FAILURE));
      return ER_GENERIC_ERROR;
    }

      for (i = 1; i < table_size; i++)
    {
      tables[i - 1] = utility_get_option_string_value (arg_map, OPTION_STRING_TABLE, i);
    }
    }

  AU_DISABLE_PASSWORDS ();

  if (standby_compactdb_flag)
    {
      db_set_client_type (DB_CLIENT_TYPE_ADMIN_COMPACTDB_WOS);
    }
  else
    {
      db_set_client_type (DB_CLIENT_TYPE_ADMIN_UTILITY);
    }

  if ((error = db_login ("DBA", NULL)) || (error = db_restart (arg->argv0, TRUE, database_name)))
    {
      PRINT_AND_LOG_ERR_MSG ("%s: %s.\n", exec_name, db_error_string (3));
      status = error;
    }
  else
    {
      status = db_set_isolation (TRAN_DEFAULT_ISOLATION_LEVEL ());

      if (status == NO_ERROR)
    {
      if (class_lock_timeout > 0)
        {
          class_lock_timeout = class_lock_timeout * 1000;
        }
      if (instance_lock_timeout > 0)
        {
          instance_lock_timeout = instance_lock_timeout * 1000;
        }
      status =
        compactdb_start (verbose_flag, delete_old_repr_flag, input_filename, tables, table_size - 1,
                 maximum_processed_space, instance_lock_timeout, class_lock_timeout,
                 TRAN_DEFAULT_ISOLATION_LEVEL ());

      if (status == ER_FAILED)
        {
          PRINT_AND_LOG_ERR_MSG (msgcat_message
                     (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_FAILURE));
        }
    }

      db_shutdown ();
    }

  if (tables)
    {
      free (tables);
      tables = NULL;
    }
  return status;
}

static int
do_reclaim_addresses (OID * const *class_oids, const int num_class_oids, int *const num_classes_fully_processed,
              const bool verbose, const int class_lock_timeout)
{
  bool any_class_can_be_referenced = false;
  int i = 0;
  int error_code = NO_ERROR;

  assert (num_class_oids >= 0);
  assert (class_oids != NULL);

  assert (num_classes_fully_processed != NULL);
  *num_classes_fully_processed = 0;

  tran_reset_wait_times (class_lock_timeout);

  for (i = 0; i < num_class_oids; ++i)
    {
      char *class_name = NULL;
      int error_while_processing = NO_ERROR;
      bool correctly_processed = false;
      bool addresses_reclaimed = false;

      error_code =
    do_reclaim_class_addresses (*class_oids[i], &class_name, &any_class_can_be_referenced, &correctly_processed,
                    &addresses_reclaimed, &error_while_processing);
      if (correctly_processed)
    {
      assert (error_code == NO_ERROR);
      assert (error_while_processing == NO_ERROR);
      (*num_classes_fully_processed)++;
    }

      if (verbose)
    {
      if (class_name == NULL || strlen (class_name) == 0)
        {
          printf (msgcat_message
              (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_UNKNOWN_CLASS_NAME));
        }
      else
        {
          printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_CLASS),
              class_name);
        }
      if (correctly_processed)
        {
          if (addresses_reclaimed)
        {
          printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_RECLAIMED));
        }
          else
        {
          printf (msgcat_message
              (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_RECLAIM_SKIPPED));
        }
        }
      else
        {
          printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_RECLAIM_ERROR));
        }
    }

      if (class_name != NULL)
    {
      free_and_init (class_name);
    }
      if (error_code != NO_ERROR)
    {
      goto error_exit;
    }
    }

  return error_code;

error_exit:
  return error_code;
}

static int
is_not_system_class (MOBJ class_)
{
  return !(((SM_CLASS *) class_)->flags & SM_CLASSFLAG_SYSTEM);
}

static int
do_reclaim_class_addresses (const OID class_oid, char **class_name, bool * const any_class_can_be_referenced,
                bool * const correctly_processed, bool * const addresses_reclaimed,
                int *const error_while_processing)
{
  DB_OBJECT *class_mop = NULL;
  DB_OBJECT *parent_mop = NULL;
  SM_CLASS *class_ = NULL;
  SM_CLASS *parent_class_ = NULL;
  int error_code = NO_ERROR;
  int skipped_error_code = NO_ERROR;
  bool do_abort_on_error = true;
  bool can_reclaim_addresses = true;
  LIST_MOPS *lmops = NULL;
  HFID *hfid = NULL;
  int is_class = 0;

  assert (!OID_ISNULL (&class_oid));
  assert (any_class_can_be_referenced != NULL);
  assert (correctly_processed != NULL);
  assert (addresses_reclaimed != NULL);
  assert (error_while_processing != NULL);
  assert (class_name != NULL);

  *correctly_processed = false;
  *addresses_reclaimed = false;
  *error_while_processing = NO_ERROR;

  error_code = db_commit_transaction ();
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  error_code = db_set_isolation (TRAN_READ_COMMITTED);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  /*
   * Trying to force an ISX_LOCK on the root class. It somehow happens that
   * we are left with an IX_LOCK in the end...
   */
  if (locator_fetch_class (sm_Root_class_mop, DB_FETCH_QUERY_WRITE) == NULL)
    {
      error_code = ER_FAILED;
      goto error_exit;
    }

  class_mop = db_object ((OID *) (&class_oid));
  if (class_mop == NULL)
    {
      skipped_error_code = ER_FAILED;
      goto error_exit;
    }

  is_class = locator_is_class (class_mop, DB_FETCH_WRITE);
  if (is_class < 0)
    {
      skipped_error_code = is_class;
      goto error_exit;
    }
  if (!is_class)
    {
      skipped_error_code = ER_FAILED;
      goto error_exit;
    }

  /*
   * We need an SCH-M lock on the class to process as early as possible so that
   * other transactions don't add references to it in the schema.
   */
  class_ = (SM_CLASS *) locator_fetch_class (class_mop, DB_FETCH_WRITE);
  if (class_ == NULL)
    {
      assert (er_errid () != NO_ERROR);
      skipped_error_code = er_errid ();
      goto error_exit;
    }

  assert (*class_name == NULL);
  *class_name = strdup (sm_ch_name ((MOBJ) class_));
  if (*class_name == NULL)
    {
      error_code = ER_FAILED;
      goto error_exit;
    }

  if (class_->partition != NULL)
    {
      /*
       * If the current class is a partition of a partitioned class we need
       * to get its parent partitioned table and check for references to its
       * parent too. If table tbl has partition tbl__p__p0, a reference to tbl
       * can point to tbl__p__p0 instances too.
       */
      skipped_error_code = do_get_partition_parent (class_mop, &parent_mop);
      if (skipped_error_code != NO_ERROR)
    {
      goto error_exit;
    }
      if (parent_mop != NULL)
    {
      parent_class_ = (SM_CLASS *) locator_fetch_class (parent_mop, DB_FETCH_WRITE);
      if (parent_class_ == NULL)
        {
          assert (er_errid () != NO_ERROR);
          skipped_error_code = er_errid ();
          goto error_exit;
        }
    }
    }

  skipped_error_code = locator_flush_all_instances (class_mop, DECACHE);
  if (skipped_error_code != NO_ERROR)
    {
      goto error_exit;
    }

  if (class_->class_type != SM_CLASS_CT)
    {
      can_reclaim_addresses = false;
    }
  else
    {
      hfid = sm_ch_heap ((MOBJ) class_);
      if (HFID_IS_NULL (hfid))
    {
      can_reclaim_addresses = false;
    }
    }

  if (class_->flags & SM_CLASSFLAG_SYSTEM)
    {
      /*
       * It should be safe to process system classes also but we skip them for
       * now. Please note that class_instances_can_be_referenced () does not
       * check for references from system classes.
       * If this is ever changed please consider the impact of reusing system
       * objects OIDs.
       */
      can_reclaim_addresses = false;
    }
  else if (class_->flags & SM_CLASSFLAG_REUSE_OID)
    {
      /*
       * Nobody should be able to hold references to reusable OID tables so it
       * should be safe to reclaim their OIDs and pages no matter what.
       */
      can_reclaim_addresses = true;
    }
  else
    {
      if (*any_class_can_be_referenced)
    {
      /*
       * Some class attribute has OBJECT or SET OF OBJECT as the domain.
       * This means it can point to instances of any class so we're not
       * safe reclaiming OIDs.
       */
      can_reclaim_addresses = false;
    }
      else
    {
      bool class_can_be_referenced = false;

      /*
       * IS_LOCK should be enough for what we need but
       * locator_get_all_class_mops seems to lock the instances with the
       * lock that it has on their class. So we end up with IX_LOCK on all
       * classes in the schema...
       */

      lmops = locator_get_all_class_mops (DB_FETCH_CLREAD_INSTREAD, is_not_system_class);
      if (lmops == NULL)
        {
          skipped_error_code = ER_FAILED;
          goto error_exit;
        }

      skipped_error_code =
        class_instances_can_be_referenced (class_mop, parent_mop, &class_can_be_referenced,
                           any_class_can_be_referenced, lmops->mops, lmops->num);
      if (skipped_error_code != NO_ERROR)
        {
          goto error_exit;
        }
      /*
       * If some attribute has OBJECT or the current class as its domain
       * then it's not safe to reclaim the OIDs as some of the references
       * might point to deleted objects. We skipped the system classes as
       * they should not point to any instances of the non-system classes.
       */
      can_reclaim_addresses = !class_can_be_referenced && !*any_class_can_be_referenced;
      if (lmops != NULL)
        {
          /*
           * It should be safe now to release all the locks we hold on the
           * schema classes (except for the X_LOCK on the current class).
           * However, we don't currently have a way of releasing those
           * locks so we're stuck with them till the end of the current
           * transaction.
           */
          locator_free_list_mops (lmops);
          lmops = NULL;
        }
    }
    }

  if (can_reclaim_addresses)
    {
      assert (hfid != NULL && !HFID_IS_NULL (hfid));

      skipped_error_code = heap_reclaim_addresses (hfid);
      if (skipped_error_code != NO_ERROR)
    {
      goto error_exit;
    }
      *addresses_reclaimed = true;
    }

  error_code = db_commit_transaction ();
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }

  assert (error_code == NO_ERROR && skipped_error_code == NO_ERROR);
  *correctly_processed = true;
  class_mop = NULL;
  class_ = NULL;
  parent_mop = NULL;
  parent_class_ = NULL;
  return error_code;

error_exit:
  *error_while_processing = skipped_error_code;
  class_mop = NULL;
  class_ = NULL;
  parent_mop = NULL;
  parent_class_ = NULL;
  if (lmops != NULL)
    {
      locator_free_list_mops (lmops);
      lmops = NULL;
    }
  if (do_abort_on_error)
    {
      int tmp_error_code = NO_ERROR;

      if (skipped_error_code == ER_LK_UNILATERALLY_ABORTED || error_code == ER_LK_UNILATERALLY_ABORTED)
    {
      tmp_error_code = tran_abort_only_client (false);
    }
      else
    {
      tmp_error_code = db_abort_transaction ();
    }
      if (tmp_error_code != NO_ERROR)
    {
      if (error_code == NO_ERROR)
        {
          error_code = tmp_error_code;
        }
    }
    }
  if (skipped_error_code == NO_ERROR && error_code == NO_ERROR)
    {
      error_code = ER_FAILED;
    }
  return error_code;
}

static int
class_instances_can_be_referenced (MOP mop, MOP parent_mop, bool * const class_can_be_referenced,
                   bool * const any_class_can_be_referenced, MOP * const all_mops, const int num_mops)
{
  int error_code = NO_ERROR;
  int i = 0;

  assert (mop != NULL);
  assert (class_can_be_referenced != NULL);
  assert (any_class_can_be_referenced != NULL);
  assert (all_mops != NULL);
  assert (num_mops > 0);

  *class_can_be_referenced = false;

  for (i = 0; i < num_mops; ++i)
    {
      error_code =
    class_referenced_by_class (mop, parent_mop, all_mops[i], class_can_be_referenced, any_class_can_be_referenced);
      if (error_code != NO_ERROR)
    {
      goto error_exit;
    }
      if (*any_class_can_be_referenced)
    {
      break;
    }
    }

  return error_code;

error_exit:
  return error_code;
}

static int
class_referenced_by_class (MOP referenced_mop, MOP parent_mop, MOP referring_mop, bool * const class_can_be_referenced,
               bool * const any_class_can_be_referenced)
{
  SM_CLASS *referring_class = NULL;
  int error_code = NO_ERROR;
  SM_ATTRIBUTE *attributes_list = NULL;

  referring_class = (SM_CLASS *) locator_fetch_class (referring_mop, DB_FETCH_READ);
  if (referring_class == NULL)
    {
      assert (er_errid () != NO_ERROR);
      error_code = er_errid ();
      goto error_exit;
    }

  /*
   * System classes should not point to any instances of the non-system
   * classes.
   */
  if (referring_class->flags & SM_CLASSFLAG_SYSTEM)
    {
      goto end;
    }

  attributes_list = referring_class->attributes;
  if (referring_class->class_type == SM_CLASS_CT)
    {
      error_code =
    class_referenced_by_attributes (referenced_mop, parent_mop, attributes_list, class_can_be_referenced,
                    any_class_can_be_referenced);
      if (error_code != NO_ERROR)
    {
      goto error_exit;
    }
      if (*any_class_can_be_referenced)
    {
      goto end;
    }
    }
  else
    {
      /*
       * View attributes are not "real" references so we can safely ignore
       * them.
       */
    }

  attributes_list = referring_class->class_attributes;
  error_code =
    class_referenced_by_attributes (referenced_mop, parent_mop, attributes_list, class_can_be_referenced,
                    any_class_can_be_referenced);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }
  if (*any_class_can_be_referenced)
    {
      goto end;
    }

  attributes_list = referring_class->shared;
  error_code =
    class_referenced_by_attributes (referenced_mop, parent_mop, attributes_list, class_can_be_referenced,
                    any_class_can_be_referenced);
  if (error_code != NO_ERROR)
    {
      goto error_exit;
    }
  if (*any_class_can_be_referenced)
    {
      goto end;
    }

end:
  return error_code;

error_exit:
  if (error_code == NO_ERROR)
    {
      error_code = ER_FAILED;
    }
  return error_code;
}

static int
class_referenced_by_attributes (MOP referenced_class, MOP parent_mop, SM_ATTRIBUTE * const attributes_list,
                bool * const class_can_be_referenced, bool * const any_class_can_be_referenced)
{
  SM_ATTRIBUTE *crt_attr = NULL;

  for (crt_attr = attributes_list; crt_attr != NULL; crt_attr = (SM_ATTRIBUTE *) crt_attr->header.next)
    {
      class_referenced_by_domain (referenced_class, crt_attr->domain, class_can_be_referenced,
                  any_class_can_be_referenced);
      if (*any_class_can_be_referenced)
    {
      goto end;
    }
      if (parent_mop != NULL)
    {
      class_referenced_by_domain (parent_mop, crt_attr->domain, class_can_be_referenced,
                      any_class_can_be_referenced);
      if (*any_class_can_be_referenced)
        {
          goto end;
        }
    }
    }

end:
  return NO_ERROR;
}

static void
class_referenced_by_domain (MOP referenced_class, TP_DOMAIN * const domain, bool * const class_can_be_referenced,
                bool * const any_class_can_be_referenced)
{
  TP_DOMAIN *crt_domain = NULL;

  assert (domain != NULL);

  for (crt_domain = domain; crt_domain != NULL; crt_domain = db_domain_next (crt_domain))
    {
      const DB_TYPE type = TP_DOMAIN_TYPE (crt_domain);

      if (type == DB_TYPE_OBJECT)
    {
      DB_OBJECT *class_ = db_domain_class (crt_domain);
      if (class_ == NULL)
        {
          *any_class_can_be_referenced = true;
        }
      else if (referenced_class == class_ || db_is_subclass (referenced_class, class_) > 0)
        {
          *class_can_be_referenced = true;
        }
      else
        {
          /* Cannot reference instances of the given class. */
        }
    }
      else if (pr_is_set_type (type))
    {
      class_referenced_by_domain (referenced_class, db_domain_set (crt_domain), class_can_be_referenced,
                      any_class_can_be_referenced);
    }
      else
    {
      /* Cannot reference an object. */
    }

      if (*any_class_can_be_referenced)
    {
      return;
    }
    }
}