Skip to content

Commit 92bbf4f

Browse files
committed
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.
1 parent 685b527 commit 92bbf4f

File tree

2 files changed

+96
-87
lines changed

2 files changed

+96
-87
lines changed

storage/innobase/include/dict0mem.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,9 @@ struct dict_index_t{
10371037
return DICT_CLUSTERED == (type & (DICT_CLUSTERED | DICT_IBUF));
10381038
}
10391039

1040+
/** @return whether this is a spatial index */
1041+
bool is_spatial() const { return UNIV_UNLIKELY(type & DICT_SPATIAL); }
1042+
10401043
/** @return whether the index includes virtual columns */
10411044
bool has_virtual() const { return type & DICT_VIRTUAL; }
10421045

storage/innobase/page/page0page.cc

Lines changed: 93 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -2385,18 +2385,11 @@ page_validate(
23852385
the page record type definition */
23862386
{
23872387
const page_dir_slot_t* slot;
2388-
mem_heap_t* heap;
2389-
byte* buf;
2390-
ulint count;
2391-
ulint own_count;
2392-
ulint rec_own_count;
2393-
ulint slot_no;
2394-
ulint data_size;
23952388
const rec_t* rec;
23962389
const rec_t* old_rec = NULL;
23972390
ulint offs;
23982391
ulint n_slots;
2399-
ibool ret = FALSE;
2392+
ibool ret = TRUE;
24002393
ulint i;
24012394
ulint* offsets = NULL;
24022395
ulint* old_offsets = NULL;
@@ -2410,7 +2403,13 @@ page_validate(
24102403
if (UNIV_UNLIKELY((ibool) !!page_is_comp(page)
24112404
!= dict_table_is_comp(index->table))) {
24122405
ib::error() << "'compact format' flag mismatch";
2413-
goto func_exit2;
2406+
func_exit2:
2407+
ib::error() << "Apparent corruption in space "
2408+
<< page_get_space_id(page) << " page "
2409+
<< page_get_page_no(page)
2410+
<< " of index " << index->name
2411+
<< " of table " << index->table->name;
2412+
return FALSE;
24142413
}
24152414
if (page_is_comp(page)) {
24162415
if (UNIV_UNLIKELY(!page_simple_validate_new(page))) {
@@ -2435,19 +2434,12 @@ page_validate(
24352434
if (max_trx_id == 0 || max_trx_id > sys_max_trx_id) {
24362435
ib::error() << "PAGE_MAX_TRX_ID out of bounds: "
24372436
<< max_trx_id << ", " << sys_max_trx_id;
2438-
goto func_exit2;
2437+
ret = FALSE;
24392438
}
24402439
} else {
24412440
ut_ad(srv_force_recovery >= SRV_FORCE_NO_UNDO_LOG_SCAN);
24422441
}
24432442

2444-
heap = mem_heap_create(srv_page_size + 200);
2445-
2446-
/* The following buffer is used to check that the
2447-
records in the page record heap do not overlap */
2448-
2449-
buf = static_cast<byte*>(mem_heap_zalloc(heap, srv_page_size));
2450-
24512443
/* Check first that the record heap and the directory do not
24522444
overlap. */
24532445

@@ -2456,20 +2448,45 @@ page_validate(
24562448
if (UNIV_UNLIKELY(!(page_header_get_ptr(page, PAGE_HEAP_TOP)
24572449
<= page_dir_get_nth_slot(page, n_slots - 1)))) {
24582450

2459-
ib::warn() << "Record heap and dir overlap on space "
2460-
<< page_get_space_id(page) << " page "
2461-
<< page_get_page_no(page) << " index " << index->name
2462-
<< ", " << page_header_get_ptr(page, PAGE_HEAP_TOP)
2463-
<< ", " << page_dir_get_nth_slot(page, n_slots - 1);
2451+
ib::warn() << "Record heap and directory overlap";
2452+
goto func_exit2;
2453+
}
24642454

2465-
goto func_exit;
2455+
switch (uint16_t type = fil_page_get_type(page)) {
2456+
case FIL_PAGE_RTREE:
2457+
if (!index->is_spatial()) {
2458+
wrong_page_type:
2459+
ib::warn() << "Wrong page type " << type;
2460+
ret = FALSE;
2461+
}
2462+
break;
2463+
case FIL_PAGE_TYPE_INSTANT:
2464+
if (index->is_instant()
2465+
&& page_get_page_no(page) == index->page) {
2466+
break;
2467+
}
2468+
goto wrong_page_type;
2469+
case FIL_PAGE_INDEX:
2470+
if (index->is_spatial()) {
2471+
goto wrong_page_type;
2472+
}
2473+
if (index->is_instant()
2474+
&& page_get_page_no(page) == index->page) {
2475+
goto wrong_page_type;
2476+
}
2477+
break;
2478+
default:
2479+
goto wrong_page_type;
24662480
}
24672481

2482+
/* The following buffer is used to check that the
2483+
records in the page record heap do not overlap */
2484+
mem_heap_t* heap = mem_heap_create(srv_page_size + 200);;
2485+
byte* buf = static_cast<byte*>(mem_heap_zalloc(heap, srv_page_size));
2486+
24682487
/* Validate the record list in a loop checking also that
24692488
it is consistent with the directory. */
2470-
count = 0;
2471-
data_size = 0;
2472-
own_count = 1;
2489+
ulint count = 0, data_size = 0, own_count = 1, slot_no = 0;
24732490
slot_no = 0;
24742491
slot = page_dir_get_nth_slot(page, slot_no);
24752492

@@ -2484,11 +2501,13 @@ page_validate(
24842501
&& UNIV_UNLIKELY(rec_get_node_ptr_flag(rec)
24852502
== page_is_leaf(page))) {
24862503
ib::error() << "'node_ptr' flag mismatch";
2487-
goto func_exit;
2504+
ret = FALSE;
2505+
goto next_rec;
24882506
}
24892507

24902508
if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
2491-
goto func_exit;
2509+
ret = FALSE;
2510+
goto next_rec;
24922511
}
24932512

24942513
/* Check that the records are in the ascending order */
@@ -2500,16 +2519,10 @@ page_validate(
25002519

25012520
/* For spatial index, on nonleaf leavel, we
25022521
allow recs to be equal. */
2503-
bool rtr_equal_nodeptrs =
2504-
(ret == 0 && dict_index_is_spatial(index)
2505-
&& !page_is_leaf(page));
2522+
if (ret <= 0 && !(ret == 0 && index->is_spatial()
2523+
&& !page_is_leaf(page))) {
25062524

2507-
if (ret <= 0 && !rtr_equal_nodeptrs) {
2508-
2509-
ib::error() << "Records in wrong order on"
2510-
" space " << page_get_space_id(page)
2511-
<< " page " << page_get_page_no(page)
2512-
<< " index " << index->name;
2525+
ib::error() << "Records in wrong order";
25132526

25142527
fputs("\nInnoDB: previous record ", stderr);
25152528
/* For spatial index, print the mbr info.*/
@@ -2530,7 +2543,7 @@ page_validate(
25302543
putc('\n', stderr);
25312544
}
25322545

2533-
goto func_exit;
2546+
ret = FALSE;
25342547
}
25352548
}
25362549

@@ -2550,41 +2563,41 @@ page_validate(
25502563
offs = page_offset(rec_get_start(rec, offsets));
25512564
i = rec_offs_size(offsets);
25522565
if (UNIV_UNLIKELY(offs + i >= srv_page_size)) {
2553-
ib::error() << "Record offset out of bounds";
2554-
goto func_exit;
2566+
ib::error() << "Record offset out of bounds: "
2567+
<< offs << '+' << i;
2568+
ret = FALSE;
2569+
goto next_rec;
25552570
}
2556-
25572571
while (i--) {
25582572
if (UNIV_UNLIKELY(buf[offs + i])) {
2559-
/* No other record may overlap this */
2560-
ib::error() << "Record overlaps another";
2561-
goto func_exit;
2573+
ib::error() << "Record overlaps another: "
2574+
<< offs << '+' << i;
2575+
ret = FALSE;
2576+
break;
25622577
}
2563-
25642578
buf[offs + i] = 1;
25652579
}
25662580

2567-
if (page_is_comp(page)) {
2568-
rec_own_count = rec_get_n_owned_new(rec);
2569-
} else {
2570-
rec_own_count = rec_get_n_owned_old(rec);
2571-
}
2572-
2573-
if (UNIV_UNLIKELY(rec_own_count != 0)) {
2581+
if (ulint rec_own_count = page_is_comp(page)
2582+
? rec_get_n_owned_new(rec)
2583+
: rec_get_n_owned_old(rec)) {
25742584
/* This is a record pointed to by a dir slot */
25752585
if (UNIV_UNLIKELY(rec_own_count != own_count)) {
2576-
ib::error() << "Wrong owned count "
2577-
<< rec_own_count << ", " << own_count;
2578-
goto func_exit;
2586+
ib::error() << "Wrong owned count at " << offs
2587+
<< ": " << rec_own_count
2588+
<< ", " << own_count;
2589+
ret = FALSE;
25792590
}
25802591

25812592
if (page_dir_slot_get_rec(slot) != rec) {
25822593
ib::error() << "Dir slot does not"
2583-
" point to right rec";
2584-
goto func_exit;
2594+
" point to right rec at " << offs;
2595+
ret = FALSE;
25852596
}
25862597

2587-
page_dir_slot_check(slot);
2598+
if (ret) {
2599+
page_dir_slot_check(slot);
2600+
}
25882601

25892602
own_count = 0;
25902603
if (!page_rec_is_supremum(rec)) {
@@ -2593,6 +2606,7 @@ page_validate(
25932606
}
25942607
}
25952608

2609+
next_rec:
25962610
if (page_rec_is_supremum(rec)) {
25972611
break;
25982612
}
@@ -2617,14 +2631,14 @@ page_validate(
26172631
}
26182632
} else if (UNIV_UNLIKELY(rec_get_n_owned_old(rec) == 0)) {
26192633
n_owned_zero:
2620-
ib::error() << "n owned is zero";
2621-
goto func_exit;
2634+
ib::error() << "n owned is zero at " << offs;
2635+
ret = FALSE;
26222636
}
26232637

26242638
if (UNIV_UNLIKELY(slot_no != n_slots - 1)) {
26252639
ib::error() << "n slots wrong " << slot_no << " "
26262640
<< (n_slots - 1);
2627-
goto func_exit;
2641+
ret = FALSE;
26282642
}
26292643

26302644
if (UNIV_UNLIKELY(ulint(page_header_get_field(page, PAGE_N_RECS))
@@ -2633,65 +2647,57 @@ page_validate(
26332647
ib::error() << "n recs wrong "
26342648
<< page_header_get_field(page, PAGE_N_RECS)
26352649
+ PAGE_HEAP_NO_USER_LOW << " " << (count + 1);
2636-
goto func_exit;
2650+
ret = FALSE;
26372651
}
26382652

26392653
if (UNIV_UNLIKELY(data_size != page_get_data_size(page))) {
26402654
ib::error() << "Summed data size " << data_size
26412655
<< ", returned by func " << page_get_data_size(page);
2642-
goto func_exit;
2656+
ret = FALSE;
26432657
}
26442658

26452659
/* Check then the free list */
2646-
rec = page_header_get_ptr(page, PAGE_FREE);
2647-
2648-
while (rec != NULL) {
2660+
for (rec = page_header_get_ptr(page, PAGE_FREE);
2661+
rec;
2662+
rec = page_rec_get_next_const(rec)) {
26492663
offsets = rec_get_offsets(rec, index, offsets,
26502664
page_is_leaf(page),
26512665
ULINT_UNDEFINED, &heap);
26522666
if (UNIV_UNLIKELY(!page_rec_validate(rec, offsets))) {
2653-
2654-
goto func_exit;
2667+
ret = FALSE;
2668+
continue;
26552669
}
26562670

26572671
count++;
26582672
offs = page_offset(rec_get_start(rec, offsets));
26592673
i = rec_offs_size(offsets);
26602674
if (UNIV_UNLIKELY(offs + i >= srv_page_size)) {
2661-
ib::error() << "Record offset out of bounds";
2662-
goto func_exit;
2675+
ib::error() << "Free record offset out of bounds: "
2676+
<< offs << '+' << i;
2677+
ret = FALSE;
2678+
continue;
26632679
}
2664-
26652680
while (i--) {
2666-
26672681
if (UNIV_UNLIKELY(buf[offs + i])) {
2668-
ib::error() << "Record overlaps another"
2669-
" in free list";
2670-
goto func_exit;
2682+
ib::error() << "Free record overlaps another: "
2683+
<< offs << '+' << i;
2684+
ret = FALSE;
2685+
break;
26712686
}
2672-
26732687
buf[offs + i] = 1;
26742688
}
2675-
2676-
rec = page_rec_get_next_const(rec);
26772689
}
26782690

26792691
if (UNIV_UNLIKELY(page_dir_get_n_heap(page) != count + 1)) {
26802692
ib::error() << "N heap is wrong "
26812693
<< page_dir_get_n_heap(page) << " " << count + 1;
2682-
goto func_exit;
2694+
ret = FALSE;
26832695
}
26842696

2685-
ret = TRUE;
2686-
2687-
func_exit:
26882697
mem_heap_free(heap);
26892698

2690-
if (UNIV_UNLIKELY(ret == FALSE)) {
2691-
func_exit2:
2692-
ib::error() << "Apparent corruption in space "
2693-
<< page_get_space_id(page) << " page "
2694-
<< page_get_page_no(page) << " index " << index->name;
2699+
if (UNIV_UNLIKELY(!ret)) {
2700+
goto func_exit2;
26952701
}
26962702

26972703
return(ret);

0 commit comments

Comments
 (0)