void btr_pcur_move_to_next_page( /*=======================*/ btr_pcur_t* cursor, /* in: persistent cursor; must be on the last record of the current page */ mtr_t* mtr) /* in: mtr */ { ulint next_page_no; ulint space; page_t* page; page_t* next_page; ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); ut_ad(btr_pcur_is_after_last_on_page(cursor, mtr)); cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; page = btr_pcur_get_page(cursor); next_page_no = btr_page_get_next(page, mtr); space = buf_frame_get_space_id(page); ut_ad(next_page_no != FIL_NULL); next_page = btr_page_get(space, next_page_no, cursor->latch_mode, mtr); ut_a(page_is_comp(next_page) == page_is_comp(page)); buf_block_align(next_page)->check_index_page_at_flush = TRUE; btr_leaf_page_release(page, cursor->latch_mode, mtr); page_cur_set_before_first(next_page, btr_pcur_get_page_cur(cursor)); page_check_dir(next_page); }
/*********************************************************//** Moves the persistent cursor to the first record on the next page. Releases the latch on the current page, and bufferunfixes it. Note that there must not be modifications on the current page, as then the x-latch can be released only in mtr_commit. */ UNIV_INTERN void btr_pcur_move_to_next_page( /*=======================*/ btr_pcur_t* cursor, /*!< in: persistent cursor; must be on the last record of the current page */ mtr_t* mtr) /*!< in: mtr */ { ulint next_page_no; ulint space; ulint zip_size; page_t* page; buf_block_t* next_block; page_t* next_page; ut_a(cursor->pos_state == BTR_PCUR_IS_POSITIONED); ut_ad(cursor->latch_mode != BTR_NO_LATCHES); ut_ad(btr_pcur_is_after_last_on_page(cursor)); cursor->old_stored = BTR_PCUR_OLD_NOT_STORED; page = btr_pcur_get_page(cursor); next_page_no = btr_page_get_next(page, mtr); space = buf_block_get_space(btr_pcur_get_block(cursor)); zip_size = buf_block_get_zip_size(btr_pcur_get_block(cursor)); ut_ad(next_page_no != FIL_NULL); next_block = btr_block_get(space, zip_size, next_page_no, cursor->latch_mode, btr_pcur_get_btr_cur(cursor)->index, mtr); next_page = buf_block_get_frame(next_block); if (srv_pass_corrupt_table && !next_page) { btr_leaf_page_release(btr_pcur_get_block(cursor), cursor->latch_mode, mtr); btr_pcur_get_page_cur(cursor)->block = 0; btr_pcur_get_page_cur(cursor)->rec = 0; return; } ut_a(next_page); #ifdef UNIV_BTR_DEBUG ut_a(page_is_comp(next_page) == page_is_comp(page)); ut_a(btr_page_get_prev(next_page, mtr) == buf_block_get_page_no(btr_pcur_get_block(cursor))); #endif /* UNIV_BTR_DEBUG */ next_block->check_index_page_at_flush = TRUE; btr_leaf_page_release(btr_pcur_get_block(cursor), cursor->latch_mode, mtr); page_cur_set_before_first(next_block, btr_pcur_get_page_cur(cursor)); page_check_dir(next_page); }
inline bool check_page_format(page_t *page) { if (process_redundant && page_is_comp(page)) { if (debug) printf("Page is in COMPACT format while we're looking for REDUNDANT - skipping\n"); return FALSE; } if (process_compact && !page_is_comp(page)) { if (debug) printf("Page is in REDUNDANT format while we're looking for COMPACT - skipping\n"); return FALSE; } return TRUE; }
int check_page(page_t *page, unsigned int *n_records){ int comp = page_is_comp(page); int16_t i, s, p, b, p_prev; int recs = 0; int max_recs = UNIV_PAGE_SIZE / 5; *n_records = 0; i = (comp) ? PAGE_NEW_INFIMUM : PAGE_OLD_INFIMUM; s = (comp) ? PAGE_NEW_SUPREMUM : PAGE_OLD_SUPREMUM; if(deleted_records_only == 1){ if (debug) printf("We look for deleted records only. Consider all pages are not valid\n"); return 0; } if (debug) printf("Checking a page\nInfimum offset: 0x%X\nSupremum offset: 0x%X\n", i, s); p_prev = 0; p = i; while(p != s){ if(recs > max_recs){ *n_records = 0; if (debug) printf("Page is bad\n"); return 0; } // If a pointer to the next record is negative - the page is bad if(p < 2){ *n_records = 0; if (debug) printf("Page is bad\n"); return 0; } // If the pointer is bigger than UNIV_PAGE_SIZE, the page is corrupted if(p > UNIV_PAGE_SIZE){ *n_records = 0; if (debug) printf("Page is bad\n"); return 0; } // If we've already was here, the page is bad if(p == p_prev){ *n_records = 0; if (debug) printf("Page is bad\n"); return 0; } p_prev = p; // Get next pointer if(comp){ b = mach_read_from_2(page + p - 2); p = p + b; } else{ p = mach_read_from_2(page + p - 2); } if (debug) printf("Next record at offset: 0x%X (%d) \n", 0x0000FFFF & p, p); recs++; } *n_records = recs -1; // - infinum record if (debug) printf("Page is good\n"); return 1; }
inline ibool check_for_a_record(page_t *page, rec_t *rec, table_def_t *table, ulint *offsets) { ulint offset, data_size; // Check if given origin is valid offset = rec - page; if (offset < record_extra_bytes + table->min_rec_header_len) return FALSE; if (debug) printf("ORIGIN=OK "); // Skip non-deleted records if (deleted_records_only && !rec_get_deleted_flag(rec, page_is_comp(page))) return FALSE; if (debug) printf("DELETED=OK "); // Skip non-deleted records if (undeleted_records_only && rec_get_deleted_flag(rec, page_is_comp(page))) return FALSE; if (debug) printf("UNDELETED=OK "); // Get field offsets for current table if (process_compact && !ibrec_init_offsets_new(page, rec, table, offsets)) return FALSE; if (process_redundant && !ibrec_init_offsets_old(page, rec, table, offsets)) return FALSE; if (debug) printf("OFFSETS=OK "); // Check the record's data size data_size = rec_offs_data_size(offsets); if (data_size > table->data_max_size) { if (debug) printf("DATA_SIZE=FAIL(%lu > %lu) ", (long int)data_size, (long int)table->data_max_size); return FALSE; } if (data_size < table->data_min_size) { if (debug) printf("DATA_SIZE=FAIL(%lu < %lu) ", (long int)data_size, (long int)table->data_min_size); return FALSE; } if (debug) printf("DATA_SIZE=OK "); // Check fields sizes if (!check_fields_sizes(rec, table, offsets)) return FALSE; if (debug) printf("FIELD_SIZES=OK "); // This record could be valid and useful for us return TRUE; }
ulint dict_truncate_index_tree( /*=====================*/ /* out: new root page number, or FIL_NULL on failure */ dict_table_t* table, /* in: the table the index belongs to */ btr_pcur_t* pcur, /* in/out: persistent cursor pointing to record in the clustered index of SYS_INDEXES table. The cursor may be repositioned in this call. */ mtr_t* mtr) /* in: mtr having the latch on the record page. The mtr may be committed and restarted in this call. */ { ulint root_page_no; ulint space; ulint type; dulint index_id; rec_t* rec; byte* ptr; ulint len; ulint comp; dict_index_t* index; ut_ad(mutex_own(&(dict_sys->mutex))); ut_a(!dict_table_is_comp(dict_sys->sys_indexes)); rec = btr_pcur_get_rec(pcur); ptr = rec_get_nth_field_old(rec, DICT_SYS_INDEXES_PAGE_NO_FIELD, &len); ut_ad(len == 4); root_page_no = mtr_read_ulint(ptr, MLOG_4BYTES, mtr); if (root_page_no == FIL_NULL) { /* The tree has been freed. */ ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: Trying to TRUNCATE" " a missing index of table %s!\n", table->name); return(FIL_NULL); } ptr = rec_get_nth_field_old(rec, DICT_SYS_INDEXES_SPACE_NO_FIELD, &len); ut_ad(len == 4); space = mtr_read_ulint(ptr, MLOG_4BYTES, mtr); if (!fil_tablespace_exists_in_mem(space)) { /* It is a single table tablespace and the .ibd file is missing: do nothing */ ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: Trying to TRUNCATE" " a missing .ibd file of table %s!\n", table->name); return(FIL_NULL); } ptr = rec_get_nth_field_old(rec, DICT_SYS_INDEXES_TYPE_FIELD, &len); ut_ad(len == 4); type = mach_read_from_4(ptr); ptr = rec_get_nth_field_old(rec, 1, &len); ut_ad(len == 8); index_id = mach_read_from_8(ptr); /* We free all the pages but the root page first; this operation may span several mini-transactions */ btr_free_but_not_root(space, root_page_no); /* Then we free the root page in the same mini-transaction where we create the b-tree and write its new root page number to the appropriate field in the SYS_INDEXES record: this mini-transaction marks the B-tree totally truncated */ comp = page_is_comp(btr_page_get(space, root_page_no, RW_X_LATCH, mtr)); btr_free_root(space, root_page_no, mtr); /* We will temporarily write FIL_NULL to the PAGE_NO field in SYS_INDEXES, so that the database will not get into an inconsistent state in case it crashes between the mtr_commit() below and the following mtr_commit() call. */ page_rec_write_index_page_no(rec, DICT_SYS_INDEXES_PAGE_NO_FIELD, FIL_NULL, mtr); /* We will need to commit the mini-transaction in order to avoid deadlocks in the btr_create() call, because otherwise we would be freeing and allocating pages in the same mini-transaction. */ btr_pcur_store_position(pcur, mtr); mtr_commit(mtr); mtr_start(mtr); btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur, mtr); /* Find the index corresponding to this SYS_INDEXES record. */ for (index = UT_LIST_GET_FIRST(table->indexes); index; index = UT_LIST_GET_NEXT(indexes, index)) { if (!ut_dulint_cmp(index->id, index_id)) { break; } } root_page_no = btr_create(type, space, index_id, comp, mtr); if (index) { index->page = (unsigned int) root_page_no; } else { ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: Index %lu %lu of table %s is missing\n" "InnoDB: from the data dictionary during TRUNCATE!\n", ut_dulint_get_high(index_id), ut_dulint_get_low(index_id), table->name); } return(root_page_no); }
void process_ibpage(page_t *page) { ulint page_id; rec_t *origin; ulint offsets[MAX_TABLE_FIELDS + 2]; ulint offset, i; int is_page_valid = 0; int comp; unsigned int expected_records = 0; unsigned int actual_records = 0; int16_t b, infimum, supremum; // Skip tables if filter used if (use_filter_id) { dulint index_id = mach_read_from_8(page + PAGE_HEADER + PAGE_INDEX_ID); if (index_id.low != filter_id.low || index_id.high != filter_id.high) { if (debug) { page_id = mach_read_from_4(page + FIL_PAGE_OFFSET); printf("Skipped using index id filter: %lu!\n", page_id); } return; } } // Read page id page_id = mach_read_from_4(page + FIL_PAGE_OFFSET); if (debug) printf("Page id: %lu\n", page_id); fprintf(f_result, "-- Page id: %lu", page_id); // Check requested and actual formats if (!check_page_format(page)) return; if(table_definitions_cnt == 0){ fprintf(stderr, "There are no table definitions. Please check include/table_defs.h\n"); exit(EXIT_FAILURE); } is_page_valid = check_page(page, &expected_records); // comp == 1 if page in COMPACT format and 0 if REDUNDANT comp = page_is_comp(page); fprintf(f_result, ", Format: %s", (comp ) ? "COMPACT": "REDUNDANT"); infimum = (comp) ? PAGE_NEW_INFIMUM : PAGE_OLD_INFIMUM; supremum = (comp) ? PAGE_NEW_SUPREMUM : PAGE_OLD_SUPREMUM; // Find possible data area start point (at least 5 bytes of utility data) if(is_page_valid){ b = mach_read_from_2(page + infimum - 2); offset = (comp) ? infimum + b : b; } else{ offset = 100 + record_extra_bytes; } fprintf(f_result, ", Records list: %s", is_page_valid? "Valid": "Invalid"); fprintf(f_result, ", Expected records: (%u %lu)", expected_records, mach_read_from_2(page + PAGE_HEADER + PAGE_N_RECS)); fprintf(f_result, "\n"); if (debug) printf("Starting offset: %lu (%lX). Checking %d table definitions.\n", offset, offset, table_definitions_cnt); // Walk through all possible positions to the end of page // (start of directory - extra bytes of the last rec) //is_page_valid = 0; while (offset < UNIV_PAGE_SIZE - record_extra_bytes && ( (offset != supremum ) || !is_page_valid) ) { // Get record pointer origin = page + offset; if (debug) printf("\nChecking offset: 0x%lX: ", offset); // Check all tables for (i = 0; i < table_definitions_cnt; i++) { // Get table info table_def_t *table = &(table_definitions[i]); if (debug) printf(" (%s) ", table->name); // Check if origin points to a valid record if (check_for_a_record(page, origin, table, offsets) && check_constraints(origin, table, offsets)) { actual_records++; if (debug) printf("\n---------------------------------------------------\n" "PAGE%lu: Found a table %s record: %p (offset = %lu)\n", \ page_id, table->name, origin, offset); if(is_page_valid){ process_ibrec(page, origin, table, offsets); b = mach_read_from_2(page + offset - 2); offset = (comp) ? offset + b : b; } else{ offset += process_ibrec(page, origin, table, offsets); } if (debug) printf("Next offset: 0x%lX", offset); break; } else{ if(is_page_valid){ b = mach_read_from_2(page + offset - 2); offset = (comp) ? offset + b : b; } else{ offset++; } if (debug) printf("\nNext offset: %lX", offset); } } } fprintf(f_result, "-- Page id: %lu", page_id); fprintf(f_result, ", Found records: %u", actual_records); fprintf(f_result, ", Lost records: %s", (actual_records != expected_records) ? "YES": "NO"); fprintf(f_result, ", Leaf page: %s", (mach_read_from_2(page + PAGE_HEADER + PAGE_LEVEL) == 0)? "YES": "NO"); fprintf(f_result, "\n"); }