/* * 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, *next_oid = 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 ((float) 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); 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], true); 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 * 1000, class_lock_timeout * 1000, 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 ((float) 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, (float) 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 ((float) 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; }
/* * process_value () - process a value * * return : error status * value(in,out) - the processed value * */ static int process_value (DB_VALUE * value) { int return_value = 0; switch (DB_VALUE_TYPE (value)) { case DB_TYPE_OID: { OID *ref_oid; OID ref_class_oid; ref_oid = DB_GET_OID (value); if (OID_ISNULL (ref_oid)) { break; } if (!heap_get_class_oid (NULL, ref_oid, &ref_class_oid)) { OID_SET_NULL (ref_oid); return_value = 1; break; } if (is_class (ref_oid, &ref_class_oid)) { break; } #if defined(CUBRID_DEBUG) printf (msgcat_message (MSGCAT_CATALOG_UTILS, MSGCAT_UTIL_SET_COMPACTDB, COMPACTDB_MSG_REFOID), ref_oid->volid, ref_oid->pageid, ref_oid->slotid, ref_class_oid.volid, ref_class_oid.pageid, ref_class_oid.slotid); #endif if (!heap_does_exist (NULL, ref_oid, &ref_class_oid)) { OID_SET_NULL (ref_oid); return_value = 1; } break; } case DB_TYPE_POINTER: case DB_TYPE_MULTISET: case DB_TYPE_SEQUENCE: case DB_TYPE_SET: { return_value = process_set (DB_GET_SET (value)); break; } default: break; } return return_value; }
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; 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; } if (!locator_is_class (class_mop, DB_FETCH_WRITE)) { skipped_error_code = ER_FAILED; goto error_exit; } /* * We need an X_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) { skipped_error_code = er_errid (); goto error_exit; } assert (*class_name == NULL); *class_name = strdup (class_->header.name); if (*class_name == NULL) { error_code = ER_FAILED; goto error_exit; } if (class_->partition_of != 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) { skipped_error_code = er_errid (); goto error_exit; } } } skipped_error_code = locator_flush_all_instances (class_mop, true); if (skipped_error_code != NO_ERROR) { goto error_exit; } if (class_->class_type != SM_CLASS_CT) { can_reclaim_addresses = false; } else { hfid = sm_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; }
/* * boot_compact_db - compact specified classes * HEAP_CACHE_ATTRINFO structure * return: error status * class_oids(in): the classes list * n_classes(in): the class_oids length * hfids(in): the hfid list * space_to_process(in): the space to process * instance_lock_timeout(in): the lock timeout for instances * class_lock_timeout(in): the lock timeout for instances * delete_old_repr(in): whether to delete the old class representation * last_processed_class_oid(in,out): last processed class oid * last_processed_oid(in,out): last processed oid * total_objects(out): count processed objects for each class * failed_objects(out): count failed objects for each class * modified_objects(out): count modified objects for each class * big_objects(out): count big objects for each class * initial_last_repr_id(in, out): the list of initial last class * representation */ int boot_compact_db (THREAD_ENTRY * thread_p, OID * class_oids, int n_classes, int space_to_process, int instance_lock_timeout, int class_lock_timeout, bool delete_old_repr, OID * last_processed_class_oid, OID * last_processed_oid, int *total_objects, int *failed_objects, int *modified_objects, int *big_objects, int *initial_last_repr_id) { int result = NO_ERROR; int i, j, start_index = -1; int max_space_to_process, current_tran_index = -1; int lock_ret; HFID hfid; if (boot_can_compact (thread_p) == false) { return ER_COMPACTDB_ALREADY_STARTED; } if (class_oids == NULL || n_classes <= 0 || space_to_process <= 0 || last_processed_class_oid == NULL || last_processed_oid == NULL || total_objects == NULL || failed_objects == NULL || modified_objects == NULL || big_objects == NULL || initial_last_repr_id == NULL) { return ER_QPROC_INVALID_PARAMETER; } for (start_index = 0; start_index < n_classes; start_index++) { if (OID_EQ (class_oids + start_index, last_processed_class_oid)) { break; } } if (start_index == n_classes) { return ER_QPROC_INVALID_PARAMETER; } for (i = 0; i < n_classes; i++) { total_objects[i] = 0; failed_objects[i] = 0; modified_objects[i] = 0; big_objects[i] = 0; } max_space_to_process = space_to_process; for (i = start_index; i < n_classes; i++) { lock_ret = lock_object_waitsecs (thread_p, class_oids + i, oid_Root_class_oid, IX_LOCK, LK_UNCOND_LOCK, class_lock_timeout); if (lock_ret != LK_GRANTED) { total_objects[i] = COMPACTDB_LOCKED_CLASS; OID_SET_NULL (last_processed_oid); continue; } if (heap_get_hfid_from_class_oid (thread_p, class_oids + i, &hfid) != NO_ERROR) { lock_unlock_object (thread_p, class_oids + i, oid_Root_class_oid, IX_LOCK, true); OID_SET_NULL (last_processed_oid); total_objects[i] = COMPACTDB_INVALID_CLASS; continue; } if (HFID_IS_NULL (&hfid)) { lock_unlock_object (thread_p, class_oids + i, oid_Root_class_oid, IX_LOCK, true); OID_SET_NULL (last_processed_oid); total_objects[i] = COMPACTDB_INVALID_CLASS; continue; } if (OID_ISNULL (last_processed_oid)) { initial_last_repr_id[i] = heap_get_class_repr_id (thread_p, class_oids + i); if (initial_last_repr_id[i] <= 0) { lock_unlock_object (thread_p, class_oids + i, oid_Root_class_oid, IX_LOCK, true); total_objects[i] = COMPACTDB_INVALID_CLASS; continue; } } if (process_class (thread_p, class_oids + i, &hfid, max_space_to_process, &instance_lock_timeout, &space_to_process, last_processed_oid, total_objects + i, failed_objects + i, modified_objects + i, big_objects + i) != NO_ERROR) { OID_SET_NULL (last_processed_oid); for (j = start_index; j <= i; j++) { total_objects[j] = COMPACTDB_UNPROCESSED_CLASS; failed_objects[j] = 0; modified_objects[j] = 0; big_objects[j] = 0; } result = ER_FAILED; break; } if (delete_old_repr && OID_ISNULL (last_processed_oid) && failed_objects[i] == 0 && heap_get_class_repr_id (thread_p, class_oids + i) == initial_last_repr_id[i]) { lock_ret = lock_object_waitsecs (thread_p, class_oids + i, oid_Root_class_oid, X_LOCK, LK_UNCOND_LOCK, class_lock_timeout); if (lock_ret == LK_GRANTED) { if (catalog_drop_old_representations (thread_p, class_oids + i) != NO_ERROR) { for (j = start_index; j <= i; j++) { total_objects[j] = COMPACTDB_UNPROCESSED_CLASS; failed_objects[j] = 0; modified_objects[j] = 0; big_objects[j] = 0; } result = ER_FAILED; } else { initial_last_repr_id[i] = COMPACTDB_REPR_DELETED; } break; } } if (space_to_process == 0) { break; } } if (OID_ISNULL (last_processed_oid)) { if (i < n_classes - 1) { COPY_OID (last_processed_class_oid, class_oids + i + 1); } else { OID_SET_NULL (last_processed_class_oid); } } else { COPY_OID (last_processed_class_oid, class_oids + i); } return result; }