Commit 92bbf4f5 authored by Marko Mäkelä's avatar Marko Mäkelä

MDEV-19916: Improve page_validate()

page_validate(): Validate also the page type, and try to list all
errors that were encountered for the page, with a little more detail.
parent 685b527f
...@@ -1037,6 +1037,9 @@ struct dict_index_t{ ...@@ -1037,6 +1037,9 @@ struct dict_index_t{
return DICT_CLUSTERED == (type & (DICT_CLUSTERED | DICT_IBUF)); return DICT_CLUSTERED == (type & (DICT_CLUSTERED | DICT_IBUF));
} }
/** @return whether this is a spatial index */
bool is_spatial() const { return UNIV_UNLIKELY(type & DICT_SPATIAL); }
/** @return whether the index includes virtual columns */ /** @return whether the index includes virtual columns */
bool has_virtual() const { return type & DICT_VIRTUAL; } bool has_virtual() const { return type & DICT_VIRTUAL; }
......
...@@ -2385,18 +2385,11 @@ page_validate( ...@@ -2385,18 +2385,11 @@ page_validate(
the page record type definition */ the page record type definition */
{ {
const page_dir_slot_t* slot; const page_dir_slot_t* slot;
mem_heap_t* heap;
byte* buf;
ulint count;
ulint own_count;
ulint rec_own_count;
ulint slot_no;
ulint data_size;
const rec_t* rec; const rec_t* rec;
const rec_t* old_rec = NULL; const rec_t* old_rec = NULL;
ulint offs; ulint offs;
ulint n_slots; ulint n_slots;
ibool ret = FALSE; ibool ret = TRUE;
ulint i; ulint i;
ulint* offsets = NULL; ulint* offsets = NULL;
ulint* old_offsets = NULL; ulint* old_offsets = NULL;
...@@ -2410,7 +2403,13 @@ page_validate( ...@@ -2410,7 +2403,13 @@ page_validate(
if (UNIV_UNLIKELY((ibool) !!page_is_comp(page) if (UNIV_UNLIKELY((ibool) !!page_is_comp(page)
!= dict_table_is_comp(index->table))) { != dict_table_is_comp(index->table))) {
ib::error() << "'compact format' flag mismatch"; ib::error() << "'compact format' flag mismatch";
goto func_exit2; func_exit2:
ib::error() << "Apparent corruption in space "
<< page_get_space_id(page) << " page "
<< page_get_page_no(page)
<< " of index " << index->name
<< " of table " << index->table->name;
return FALSE;
} }
if (page_is_comp(page)) { if (page_is_comp(page)) {
if (UNIV_UNLIKELY(!page_simple_validate_new(page))) { if (UNIV_UNLIKELY(!page_simple_validate_new(page))) {
...@@ -2435,19 +2434,12 @@ page_validate( ...@@ -2435,19 +2434,12 @@ page_validate(
if (max_trx_id == 0 || max_trx_id > sys_max_trx_id) { if (max_trx_id == 0 || max_trx_id > sys_max_trx_id) {
ib::error() << "PAGE_MAX_TRX_ID out of bounds: " ib::error() << "PAGE_MAX_TRX_ID out of bounds: "
<< max_trx_id << ", " << sys_max_trx_id; << max_trx_id << ", " << sys_max_trx_id;
goto func_exit2; ret = FALSE;
} }
} else { } else {
ut_ad(srv_force_recovery >= SRV_FORCE_NO_UNDO_LOG_SCAN); ut_ad(srv_force_recovery >= SRV_FORCE_NO_UNDO_LOG_SCAN);
} }
heap = mem_heap_create(srv_page_size + 200);
/* The following buffer is used to check that the
records in the page record heap do not overlap */
buf = static_cast<byte*>(mem_heap_zalloc(heap, srv_page_size));
/* Check first that the record heap and the directory do not /* Check first that the record heap and the directory do not
overlap. */ overlap. */
...@@ -2456,20 +2448,45 @@ page_validate( ...@@ -2456,20 +2448,45 @@ page_validate(
if (UNIV_UNLIKELY(!(page_header_get_ptr(page, PAGE_HEAP_TOP) if (UNIV_UNLIKELY(!(page_header_get_ptr(page, PAGE_HEAP_TOP)
<= page_dir_get_nth_slot(page, n_slots - 1)))) { <= page_dir_get_nth_slot(page, n_slots - 1)))) {
ib::warn() << "Record heap and dir overlap on space " ib::warn() << "Record heap and directory overlap";
<< page_get_space_id(page) << " page " goto func_exit2;
<< page_get_page_no(page) << " index " << index->name }
<< ", " << page_header_get_ptr(page, PAGE_HEAP_TOP)
<< ", " << page_dir_get_nth_slot(page, n_slots - 1);
goto func_exit; switch (uint16_t type = fil_page_get_type(page)) {
case FIL_PAGE_RTREE:
if (!index->is_spatial()) {
wrong_page_type:
ib::warn() << "Wrong page type " << type;
ret = FALSE;
} }
break;
case FIL_PAGE_TYPE_INSTANT:
if (index->is_instant()
&& page_get_page_no(page) == index->page) {
break;
}
goto wrong_page_type;
case FIL_PAGE_INDEX:
if (index->is_spatial()) {
goto wrong_page_type;
}
if (index->is_instant()
&& page_get_page_no(page) == index->page) {
goto wrong_page_type;
}
break;
default:
goto wrong_page_type;
}
/* The following buffer is used to check that the
records in the page record heap do not overlap */
mem_heap_t* heap = mem_heap_create(srv_page_size + 200);;
byte* buf = static_cast<byte*>(mem_heap_zalloc(heap, srv_page_size));
/* Validate the record list in a loop checking also that /* Validate the record list in a loop checking also that
it is consistent with the directory. */ it is consistent with the directory. */
count = 0; ulint count = 0, data_size = 0, own_count = 1, slot_no = 0;
data_size = 0;
own_count = 1;
slot_no = 0; slot_no = 0;
slot = page_dir_get_nth_slot(page, slot_no); slot = page_dir_get_nth_slot(page, slot_no);
...@@ -2484,11 +2501,13 @@ page_validate( ...@@ -2484,11 +2501,13 @@ page_validate(
&& UNIV_UNLIKELY(rec_get_node_ptr_flag(rec) && UNIV_UNLIKELY(rec_get_node_ptr_flag(rec)
== page_is_leaf(page))) { == page_is_leaf(page))) {
ib::error() << "'node_ptr' flag mismatch"; ib::error() << "'node_ptr' flag mismatch";
goto func_exit; ret = FALSE;
goto next_rec;
} }
if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) { if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
goto func_exit; ret = FALSE;
goto next_rec;
} }
/* Check that the records are in the ascending order */ /* Check that the records are in the ascending order */
...@@ -2500,16 +2519,10 @@ page_validate( ...@@ -2500,16 +2519,10 @@ page_validate(
/* For spatial index, on nonleaf leavel, we /* For spatial index, on nonleaf leavel, we
allow recs to be equal. */ allow recs to be equal. */
bool rtr_equal_nodeptrs = if (ret <= 0 && !(ret == 0 && index->is_spatial()
(ret == 0 && dict_index_is_spatial(index) && !page_is_leaf(page))) {
&& !page_is_leaf(page));
if (ret <= 0 && !rtr_equal_nodeptrs) { ib::error() << "Records in wrong order";
ib::error() << "Records in wrong order on"
" space " << page_get_space_id(page)
<< " page " << page_get_page_no(page)
<< " index " << index->name;
fputs("\nInnoDB: previous record ", stderr); fputs("\nInnoDB: previous record ", stderr);
/* For spatial index, print the mbr info.*/ /* For spatial index, print the mbr info.*/
...@@ -2530,7 +2543,7 @@ page_validate( ...@@ -2530,7 +2543,7 @@ page_validate(
putc('\n', stderr); putc('\n', stderr);
} }
goto func_exit; ret = FALSE;
} }
} }
...@@ -2550,41 +2563,41 @@ page_validate( ...@@ -2550,41 +2563,41 @@ page_validate(
offs = page_offset(rec_get_start(rec, offsets)); offs = page_offset(rec_get_start(rec, offsets));
i = rec_offs_size(offsets); i = rec_offs_size(offsets);
if (UNIV_UNLIKELY(offs + i >= srv_page_size)) { if (UNIV_UNLIKELY(offs + i >= srv_page_size)) {
ib::error() << "Record offset out of bounds"; ib::error() << "Record offset out of bounds: "
goto func_exit; << offs << '+' << i;
ret = FALSE;
goto next_rec;
} }
while (i--) { while (i--) {
if (UNIV_UNLIKELY(buf[offs + i])) { if (UNIV_UNLIKELY(buf[offs + i])) {
/* No other record may overlap this */ ib::error() << "Record overlaps another: "
ib::error() << "Record overlaps another"; << offs << '+' << i;
goto func_exit; ret = FALSE;
break;
} }
buf[offs + i] = 1; buf[offs + i] = 1;
} }
if (page_is_comp(page)) { if (ulint rec_own_count = page_is_comp(page)
rec_own_count = rec_get_n_owned_new(rec); ? rec_get_n_owned_new(rec)
} else { : rec_get_n_owned_old(rec)) {
rec_own_count = rec_get_n_owned_old(rec);
}
if (UNIV_UNLIKELY(rec_own_count != 0)) {
/* This is a record pointed to by a dir slot */ /* This is a record pointed to by a dir slot */
if (UNIV_UNLIKELY(rec_own_count != own_count)) { if (UNIV_UNLIKELY(rec_own_count != own_count)) {
ib::error() << "Wrong owned count " ib::error() << "Wrong owned count at " << offs
<< rec_own_count << ", " << own_count; << ": " << rec_own_count
goto func_exit; << ", " << own_count;
ret = FALSE;
} }
if (page_dir_slot_get_rec(slot) != rec) { if (page_dir_slot_get_rec(slot) != rec) {
ib::error() << "Dir slot does not" ib::error() << "Dir slot does not"
" point to right rec"; " point to right rec at " << offs;
goto func_exit; ret = FALSE;
} }
if (ret) {
page_dir_slot_check(slot); page_dir_slot_check(slot);
}
own_count = 0; own_count = 0;
if (!page_rec_is_supremum(rec)) { if (!page_rec_is_supremum(rec)) {
...@@ -2593,6 +2606,7 @@ page_validate( ...@@ -2593,6 +2606,7 @@ page_validate(
} }
} }
next_rec:
if (page_rec_is_supremum(rec)) { if (page_rec_is_supremum(rec)) {
break; break;
} }
...@@ -2617,14 +2631,14 @@ page_validate( ...@@ -2617,14 +2631,14 @@ page_validate(
} }
} else if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) { } else if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) {
n_owned_zero: n_owned_zero:
ib::error() << "n owned is zero"; ib::error() << "n owned is zero at " << offs;
goto func_exit; ret = FALSE;
} }
if (UNIV_UNLIKELY(slot_no != n_slots - 1)) { if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
ib::error() << "n slots wrong " << slot_no << " " ib::error() << "n slots wrong " << slot_no << " "
<< (n_slots - 1); << (n_slots - 1);
goto func_exit; ret = FALSE;
} }
if (UNIV_UNLIKELY(ulint(page_header_get_field(page, PAGE_N_RECS)) if (UNIV_UNLIKELY(ulint(page_header_get_field(page, PAGE_N_RECS))
...@@ -2633,65 +2647,57 @@ page_validate( ...@@ -2633,65 +2647,57 @@ page_validate(
ib::error() << "n recs wrong " ib::error() << "n recs wrong "
<< page_header_get_field(page, PAGE_N_RECS) << page_header_get_field(page, PAGE_N_RECS)
+ PAGE_HEAP_NO_USER_LOW << " " << (count + 1); + PAGE_HEAP_NO_USER_LOW << " " << (count + 1);
goto func_exit; ret = FALSE;
} }
if (UNIV_UNLIKELY(data_size != page_get_data_size(page))) { if (UNIV_UNLIKELY(data_size != page_get_data_size(page))) {
ib::error() << "Summed data size " << data_size ib::error() << "Summed data size " << data_size
<< ", returned by func " << page_get_data_size(page); << ", returned by func " << page_get_data_size(page);
goto func_exit; ret = FALSE;
} }
/* Check then the free list */ /* Check then the free list */
rec = page_header_get_ptr(page, PAGE_FREE); for (rec = page_header_get_ptr(page, PAGE_FREE);
rec;
while (rec != NULL) { rec = page_rec_get_next_const(rec)) {
offsets = rec_get_offsets(rec, index, offsets, offsets = rec_get_offsets(rec, index, offsets,
page_is_leaf(page), page_is_leaf(page),
ULINT_UNDEFINED, &heap); ULINT_UNDEFINED, &heap);
if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) { if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
ret = FALSE;
goto func_exit; continue;
} }
count++; count++;
offs = page_offset(rec_get_start(rec, offsets)); offs = page_offset(rec_get_start(rec, offsets));
i = rec_offs_size(offsets); i = rec_offs_size(offsets);
if (UNIV_UNLIKELY(offs + i >= srv_page_size)) { if (UNIV_UNLIKELY(offs + i >= srv_page_size)) {
ib::error() << "Record offset out of bounds"; ib::error() << "Free record offset out of bounds: "
goto func_exit; << offs << '+' << i;
ret = FALSE;
continue;
} }
while (i--) { while (i--) {
if (UNIV_UNLIKELY(buf[offs + i])) { if (UNIV_UNLIKELY(buf[offs + i])) {
ib::error() << "Record overlaps another" ib::error() << "Free record overlaps another: "
" in free list"; << offs << '+' << i;
goto func_exit; ret = FALSE;
break;
} }
buf[offs + i] = 1; buf[offs + i] = 1;
} }
rec = page_rec_get_next_const(rec);
} }
if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) { if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
ib::error() << "N heap is wrong " ib::error() << "N heap is wrong "
<< page_dir_get_n_heap(page) << " " << count + 1; << page_dir_get_n_heap(page) << " " << count + 1;
goto func_exit; ret = FALSE;
} }
ret = TRUE;
func_exit:
mem_heap_free(heap); mem_heap_free(heap);
if (UNIV_UNLIKELY(ret == FALSE)) { if (UNIV_UNLIKELY(!ret)) {
func_exit2: goto func_exit2;
ib::error() << "Apparent corruption in space "
<< page_get_space_id(page) << " page "
<< page_get_page_no(page) << " index " << index->name;
} }
return(ret); return(ret);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment