PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
verify_gin.c File Reference
#include "postgres.h"
#include "access/gin_private.h"
#include "access/nbtree.h"
#include "catalog/pg_am.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "verify_common.h"
#include "string.h"
Include dependency graph for verify_gin.c:

Go to the source code of this file.

Data Structures

struct  GinScanItem
 
struct  GinPostingTreeScanItem
 

Typedefs

typedef struct GinScanItem GinScanItem
 
typedef struct GinPostingTreeScanItem GinPostingTreeScanItem
 

Functions

 PG_FUNCTION_INFO_V1 (gin_index_check)
 
static void gin_check_parent_keys_consistency (Relation rel, Relation heaprel, void *callback_state, bool readonly)
 
static void check_index_page (Relation rel, Buffer buffer, BlockNumber blockNo)
 
static IndexTuple gin_refind_parent (Relation rel, BlockNumber parentblkno, BlockNumber childblkno, BufferAccessStrategy strategy)
 
static ItemId PageGetItemIdCareful (Relation rel, BlockNumber block, Page page, OffsetNumber offset)
 
Datum gin_index_check (PG_FUNCTION_ARGS)
 
static ItemPointer ginReadTupleWithoutState (IndexTuple itup, int *nitems)
 
static void gin_check_posting_tree_parent_keys_consistency (Relation rel, BlockNumber posting_tree_root)
 

Typedef Documentation

◆ GinPostingTreeScanItem

◆ GinScanItem

typedef struct GinScanItem GinScanItem

Function Documentation

◆ check_index_page()

static void check_index_page ( Relation  rel,
Buffer  buffer,
BlockNumber  blockNo 
)
static

Definition at line 669 of file verify_gin.c.

670{
671 Page page = BufferGetPage(buffer);
672
673 /*
674 * ReadBuffer verifies that every newly-read page passes
675 * PageHeaderIsValid, which means it either contains a reasonably sane
676 * page header or is all-zero. We have to defend against the all-zero
677 * case, however.
678 */
679 if (PageIsNew(page))
681 (errcode(ERRCODE_INDEX_CORRUPTED),
682 errmsg("index \"%s\" contains unexpected zero page at block %u",
684 BufferGetBlockNumber(buffer)),
685 errhint("Please REINDEX it.")));
686
687 /*
688 * Additionally check that the special area looks sane.
689 */
690 if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData)))
692 (errcode(ERRCODE_INDEX_CORRUPTED),
693 errmsg("index \"%s\" contains corrupted page at block %u",
695 BufferGetBlockNumber(buffer)),
696 errhint("Please REINDEX it.")));
697
698 if (GinPageIsDeleted(page))
699 {
700 if (!GinPageIsLeaf(page))
702 (errcode(ERRCODE_INDEX_CORRUPTED),
703 errmsg("index \"%s\" has deleted internal page %u",
704 RelationGetRelationName(rel), blockNo)));
707 (errcode(ERRCODE_INDEX_CORRUPTED),
708 errmsg("index \"%s\" has deleted page %u with tuples",
709 RelationGetRelationName(rel), blockNo)));
710 }
713 (errcode(ERRCODE_INDEX_CORRUPTED),
714 errmsg("index \"%s\" has page %u with exceeding count of tuples",
715 RelationGetRelationName(rel), blockNo)));
716}
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:4231
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:417
static uint16 PageGetSpecialSize(const PageData *page)
Definition: bufpage.h:317
static bool PageIsNew(const PageData *page)
Definition: bufpage.h:234
PageData * Page
Definition: bufpage.h:82
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition: bufpage.h:372
#define MAXALIGN(LEN)
Definition: c.h:782
int errhint(const char *fmt,...)
Definition: elog.c:1318
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define ERROR
Definition: elog.h:39
#define ereport(elevel,...)
Definition: elog.h:149
#define GinPageIsDeleted(page)
Definition: ginblock.h:124
#define GinPageIsLeaf(page)
Definition: ginblock.h:112
#define MaxIndexTuplesPerPage
Definition: itup.h:181
#define InvalidOffsetNumber
Definition: off.h:26
#define RelationGetRelationName(relation)
Definition: rel.h:550

References BufferGetBlockNumber(), BufferGetPage(), ereport, errcode(), errhint(), errmsg(), ERROR, GinPageIsDeleted, GinPageIsLeaf, InvalidOffsetNumber, MAXALIGN, MaxIndexTuplesPerPage, PageGetMaxOffsetNumber(), PageGetSpecialSize(), PageIsNew(), and RelationGetRelationName.

Referenced by gin_check_parent_keys_consistency().

◆ gin_check_parent_keys_consistency()

static void gin_check_parent_keys_consistency ( Relation  rel,
Relation  heaprel,
void *  callback_state,
bool  readonly 
)
static

Definition at line 395 of file verify_gin.c.

399{
401 GinScanItem *stack;
402 MemoryContext mctx;
403 MemoryContext oldcontext;
405 int leafdepth;
406
408 "amcheck consistency check context",
410 oldcontext = MemoryContextSwitchTo(mctx);
411 initGinState(&state, rel);
412
413 /*
414 * We don't know the height of the tree yet, but as soon as we encounter a
415 * leaf page, we will set 'leafdepth' to its depth.
416 */
417 leafdepth = -1;
418
419 /* Start the scan at the root page */
420 stack = (GinScanItem *) palloc0(sizeof(GinScanItem));
421 stack->depth = 0;
422 stack->parenttup = NULL;
425 stack->blkno = GIN_ROOT_BLKNO;
426
427 while (stack)
428 {
429 GinScanItem *stack_next;
430 Buffer buffer;
431 Page page;
433 maxoff,
434 prev_attnum;
435 XLogRecPtr lsn;
436 IndexTuple prev_tuple;
437 BlockNumber rightlink;
438
440
441 buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno,
442 RBM_NORMAL, strategy);
443 LockBuffer(buffer, GIN_SHARE);
444 page = (Page) BufferGetPage(buffer);
445 lsn = BufferGetLSNAtomic(buffer);
446 maxoff = PageGetMaxOffsetNumber(page);
447 rightlink = GinPageGetOpaque(page)->rightlink;
448
449 /* Do basic sanity checks on the page headers */
450 check_index_page(rel, buffer, stack->blkno);
451
452 elog(DEBUG3, "processing entry tree page at blk %u, maxoff: %u", stack->blkno, maxoff);
453
454 /*
455 * It's possible that the page was split since we looked at the
456 * parent, so that we didn't missed the downlink of the right sibling
457 * when we scanned the parent. If so, add the right sibling to the
458 * stack now.
459 */
460 if (stack->parenttup != NULL)
461 {
462 GinNullCategory parent_key_category;
463 Datum parent_key = gintuple_get_key(&state,
464 stack->parenttup,
465 &parent_key_category);
466 ItemId iid = PageGetItemIdCareful(rel, stack->blkno,
467 page, maxoff);
468 IndexTuple idxtuple = (IndexTuple) PageGetItem(page, iid);
470 GinNullCategory page_max_key_category;
471 Datum page_max_key = gintuple_get_key(&state, idxtuple, &page_max_key_category);
472
473 if (rightlink != InvalidBlockNumber &&
474 ginCompareEntries(&state, attnum, page_max_key,
475 page_max_key_category, parent_key,
476 parent_key_category) > 0)
477 {
478 /* split page detected, install right link to the stack */
479 GinScanItem *ptr;
480
481 elog(DEBUG3, "split detected for blk: %u, parent blk: %u", stack->blkno, stack->parentblk);
482
483 ptr = (GinScanItem *) palloc(sizeof(GinScanItem));
484 ptr->depth = stack->depth;
485 ptr->parenttup = CopyIndexTuple(stack->parenttup);
486 ptr->parentblk = stack->parentblk;
487 ptr->parentlsn = stack->parentlsn;
488 ptr->blkno = rightlink;
489 ptr->next = stack->next;
490 stack->next = ptr;
491 }
492 }
493
494 /* Check that the tree has the same height in all branches */
495 if (GinPageIsLeaf(page))
496 {
497 if (leafdepth == -1)
498 leafdepth = stack->depth;
499 else if (stack->depth != leafdepth)
501 (errcode(ERRCODE_INDEX_CORRUPTED),
502 errmsg("index \"%s\": internal pages traversal encountered leaf page unexpectedly on block %u",
503 RelationGetRelationName(rel), stack->blkno)));
504 }
505
506 /*
507 * Check that tuples in each page are properly ordered and consistent
508 * with parent high key
509 */
510 prev_tuple = NULL;
511 prev_attnum = InvalidAttrNumber;
512 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
513 {
514 ItemId iid = PageGetItemIdCareful(rel, stack->blkno, page, i);
515 IndexTuple idxtuple = (IndexTuple) PageGetItem(page, iid);
517 GinNullCategory prev_key_category;
518 Datum prev_key;
519 GinNullCategory current_key_category;
520 Datum current_key;
521
522 if (MAXALIGN(ItemIdGetLength(iid)) != MAXALIGN(IndexTupleSize(idxtuple)))
524 (errcode(ERRCODE_INDEX_CORRUPTED),
525 errmsg("index \"%s\" has inconsistent tuple sizes, block %u, offset %u",
526 RelationGetRelationName(rel), stack->blkno, i)));
527
528 current_key = gintuple_get_key(&state, idxtuple, &current_key_category);
529
530 /*
531 * First block is metadata, skip order check. Also, never check
532 * for high key on rightmost page, as this key is not really
533 * stored explicitly.
534 *
535 * Also make sure to not compare entries for different attnums,
536 * which may be stored on the same page.
537 */
538 if (i != FirstOffsetNumber && attnum == prev_attnum && stack->blkno != GIN_ROOT_BLKNO &&
539 !(i == maxoff && rightlink == InvalidBlockNumber))
540 {
541 prev_key = gintuple_get_key(&state, prev_tuple, &prev_key_category);
542 if (ginCompareEntries(&state, attnum, prev_key,
543 prev_key_category, current_key,
544 current_key_category) >= 0)
546 (errcode(ERRCODE_INDEX_CORRUPTED),
547 errmsg("index \"%s\" has wrong tuple order on entry tree page, block %u, offset %u, rightlink %u",
548 RelationGetRelationName(rel), stack->blkno, i, rightlink)));
549 }
550
551 /*
552 * Check if this tuple is consistent with the downlink in the
553 * parent.
554 */
555 if (stack->parenttup &&
556 i == maxoff)
557 {
558 GinNullCategory parent_key_category;
559 Datum parent_key = gintuple_get_key(&state,
560 stack->parenttup,
561 &parent_key_category);
562
563 if (ginCompareEntries(&state, attnum, current_key,
564 current_key_category, parent_key,
565 parent_key_category) > 0)
566 {
567 /*
568 * There was a discrepancy between parent and child
569 * tuples. We need to verify it is not a result of
570 * concurrent call of gistplacetopage(). So, lock parent
571 * and try to find downlink for current page. It may be
572 * missing due to concurrent page split, this is OK.
573 */
574 pfree(stack->parenttup);
575 stack->parenttup = gin_refind_parent(rel, stack->parentblk,
576 stack->blkno, strategy);
577
578 /* We found it - make a final check before failing */
579 if (!stack->parenttup)
580 elog(NOTICE, "Unable to find parent tuple for block %u on block %u due to concurrent split",
581 stack->blkno, stack->parentblk);
582 else
583 {
584 parent_key = gintuple_get_key(&state,
585 stack->parenttup,
586 &parent_key_category);
587
588 /*
589 * Check if it is properly adjusted. If succeed,
590 * procced to the next key.
591 */
592 if (ginCompareEntries(&state, attnum, current_key,
593 current_key_category, parent_key,
594 parent_key_category) > 0)
596 (errcode(ERRCODE_INDEX_CORRUPTED),
597 errmsg("index \"%s\" has inconsistent records on page %u offset %u",
598 RelationGetRelationName(rel), stack->blkno, i)));
599 }
600 }
601 }
602
603 /* If this is an internal page, recurse into the child */
604 if (!GinPageIsLeaf(page))
605 {
606 GinScanItem *ptr;
607
608 ptr = (GinScanItem *) palloc(sizeof(GinScanItem));
609 ptr->depth = stack->depth + 1;
610 /* last tuple in layer has no high key */
611 if (i != maxoff && !GinPageGetOpaque(page)->rightlink)
612 ptr->parenttup = CopyIndexTuple(idxtuple);
613 else
614 ptr->parenttup = NULL;
615 ptr->parentblk = stack->blkno;
616 ptr->blkno = GinGetDownlink(idxtuple);
617 ptr->parentlsn = lsn;
618 ptr->next = stack->next;
619 stack->next = ptr;
620 }
621 /* If this item is a pointer to a posting tree, recurse into it */
622 else if (GinIsPostingTree(idxtuple))
623 {
624 BlockNumber rootPostingTree = GinGetPostingTree(idxtuple);
625
627 }
628 else
629 {
630 ItemPointer ipd;
631 int nipd;
632
633 ipd = ginReadTupleWithoutState(idxtuple, &nipd);
634
635 for (int j = 0; j < nipd; j++)
636 {
639 (errcode(ERRCODE_INDEX_CORRUPTED),
640 errmsg("index \"%s\": posting list contains invalid heap pointer on block %u",
641 RelationGetRelationName(rel), stack->blkno)));
642 }
643 pfree(ipd);
644 }
645
646 prev_tuple = CopyIndexTuple(idxtuple);
647 prev_attnum = attnum;
648 }
649
650 LockBuffer(buffer, GIN_UNLOCK);
651 ReleaseBuffer(buffer);
652
653 /* Step to next item in the queue */
654 stack_next = stack->next;
655 if (stack->parenttup)
656 pfree(stack->parenttup);
657 pfree(stack);
658 stack = stack_next;
659 }
660
661 MemoryContextSwitchTo(oldcontext);
663}
#define InvalidAttrNumber
Definition: attnum.h:23
uint32 BlockNumber
Definition: block.h:31
#define InvalidBlockNumber
Definition: block.h:33
int Buffer
Definition: buf.h:23
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:5373
XLogRecPtr BufferGetLSNAtomic(Buffer buffer)
Definition: bufmgr.c:4493
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:5607
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:805
@ BAS_BULKREAD
Definition: bufmgr.h:37
@ RBM_NORMAL
Definition: bufmgr.h:46
static Item PageGetItem(const PageData *page, const ItemIdData *itemId)
Definition: bufpage.h:354
#define DEBUG3
Definition: elog.h:28
#define elog(elevel,...)
Definition: elog.h:226
#define NOTICE
Definition: elog.h:35
BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype)
Definition: freelist.c:541
#define GIN_UNLOCK
Definition: gin_private.h:49
#define GIN_SHARE
Definition: gin_private.h:50
#define GinIsPostingTree(itup)
Definition: ginblock.h:231
#define GinPageGetOpaque(page)
Definition: ginblock.h:110
#define GIN_ROOT_BLKNO
Definition: ginblock.h:52
#define GinGetDownlink(itup)
Definition: ginblock.h:257
#define GinGetPostingTree(itup)
Definition: ginblock.h:233
signed char GinNullCategory
Definition: ginblock.h:206
OffsetNumber gintuple_get_attrnum(GinState *ginstate, IndexTuple tuple)
Definition: ginutil.c:231
Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple, GinNullCategory *category)
Definition: ginutil.c:264
void initGinState(GinState *state, Relation index)
Definition: ginutil.c:102
int ginCompareEntries(GinState *ginstate, OffsetNumber attnum, Datum a, GinNullCategory categorya, Datum b, GinNullCategory categoryb)
Definition: ginutil.c:393
IndexTuple CopyIndexTuple(IndexTuple source)
Definition: indextuple.c:547
int j
Definition: isn.c:78
int i
Definition: isn.c:77
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
static OffsetNumber ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
Definition: itemptr.h:124
IndexTupleData * IndexTuple
Definition: itup.h:53
static Size IndexTupleSize(const IndexTupleData *itup)
Definition: itup.h:71
void pfree(void *pointer)
Definition: mcxt.c:2146
void * palloc0(Size size)
Definition: mcxt.c:1969
void * palloc(Size size)
Definition: mcxt.c:1939
MemoryContext CurrentMemoryContext
Definition: mcxt.c:159
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:485
#define AllocSetContextCreate
Definition: memutils.h:149
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:180
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:123
#define OffsetNumberIsValid(offsetNumber)
Definition: off.h:39
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
uint16 OffsetNumber
Definition: off.h:24
#define FirstOffsetNumber
Definition: off.h:27
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
int16 attnum
Definition: pg_attribute.h:74
uintptr_t Datum
Definition: postgres.h:69
@ MAIN_FORKNUM
Definition: relpath.h:58
XLogRecPtr parentlsn
Definition: verify_gin.c:41
IndexTuple parenttup
Definition: verify_gin.c:39
BlockNumber parentblk
Definition: verify_gin.c:40
BlockNumber blkno
Definition: verify_gin.c:42
struct GinScanItem * next
Definition: verify_gin.c:43
Definition: regguts.h:323
static IndexTuple gin_refind_parent(Relation rel, BlockNumber parentblkno, BlockNumber childblkno, BufferAccessStrategy strategy)
Definition: verify_gin.c:725
static ItemPointer ginReadTupleWithoutState(IndexTuple itup, int *nitems)
Definition: verify_gin.c:100
static void gin_check_posting_tree_parent_keys_consistency(Relation rel, BlockNumber posting_tree_root)
Definition: verify_gin.c:135
static void check_index_page(Relation rel, Buffer buffer, BlockNumber blockNo)
Definition: verify_gin.c:669
static ItemId PageGetItemIdCareful(Relation rel, BlockNumber block, Page page, OffsetNumber offset)
Definition: verify_gin.c:766
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, attnum, BAS_BULKREAD, GinScanItem::blkno, BufferGetLSNAtomic(), BufferGetPage(), CHECK_FOR_INTERRUPTS, check_index_page(), CopyIndexTuple(), CurrentMemoryContext, DEBUG3, GinScanItem::depth, elog, ereport, errcode(), errmsg(), ERROR, FirstOffsetNumber, GetAccessStrategy(), gin_check_posting_tree_parent_keys_consistency(), gin_refind_parent(), GIN_ROOT_BLKNO, GIN_SHARE, GIN_UNLOCK, ginCompareEntries(), GinGetDownlink, GinGetPostingTree, GinIsPostingTree, GinPageGetOpaque, GinPageIsLeaf, ginReadTupleWithoutState(), gintuple_get_attrnum(), gintuple_get_key(), i, IndexTupleSize(), initGinState(), InvalidAttrNumber, InvalidBlockNumber, InvalidXLogRecPtr, ItemIdGetLength, ItemPointerGetOffsetNumber(), j, LockBuffer(), MAIN_FORKNUM, MAXALIGN, MemoryContextDelete(), MemoryContextSwitchTo(), GinScanItem::next, NOTICE, OffsetNumberIsValid, OffsetNumberNext, PageGetItem(), PageGetItemIdCareful(), PageGetMaxOffsetNumber(), palloc(), palloc0(), GinScanItem::parentblk, GinScanItem::parentlsn, GinScanItem::parenttup, pfree(), RBM_NORMAL, ReadBufferExtended(), RelationGetRelationName, and ReleaseBuffer().

Referenced by gin_index_check().

◆ gin_check_posting_tree_parent_keys_consistency()

static void gin_check_posting_tree_parent_keys_consistency ( Relation  rel,
BlockNumber  posting_tree_root 
)
static

Definition at line 135 of file verify_gin.c.

136{
139 MemoryContext mctx;
140 MemoryContext oldcontext;
141
142 int leafdepth;
143
145 "posting tree check context",
147 oldcontext = MemoryContextSwitchTo(mctx);
148
149 /*
150 * We don't know the height of the tree yet, but as soon as we encounter a
151 * leaf page, we will set 'leafdepth' to its depth.
152 */
153 leafdepth = -1;
154
155 /* Start the scan at the root page */
157 stack->depth = 0;
160 stack->blkno = posting_tree_root;
161
162 elog(DEBUG3, "processing posting tree at blk %u", posting_tree_root);
163
164 while (stack)
165 {
166 GinPostingTreeScanItem *stack_next;
167 Buffer buffer;
168 Page page;
170 maxoff;
171 BlockNumber rightlink;
172
174
175 buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno,
176 RBM_NORMAL, strategy);
177 LockBuffer(buffer, GIN_SHARE);
178 page = (Page) BufferGetPage(buffer);
179
180 Assert(GinPageIsData(page));
181
182 /* Check that the tree has the same height in all branches */
183 if (GinPageIsLeaf(page))
184 {
185 ItemPointerData minItem;
186 int nlist;
188 char tidrange_buf[MAXPGPATH];
189
190 ItemPointerSetMin(&minItem);
191
192 elog(DEBUG1, "page blk: %u, type leaf", stack->blkno);
193
194 if (leafdepth == -1)
195 leafdepth = stack->depth;
196 else if (stack->depth != leafdepth)
198 (errcode(ERRCODE_INDEX_CORRUPTED),
199 errmsg("index \"%s\": internal pages traversal encountered leaf page unexpectedly on block %u",
200 RelationGetRelationName(rel), stack->blkno)));
201 list = GinDataLeafPageGetItems(page, &nlist, minItem);
202
203 if (nlist > 0)
204 snprintf(tidrange_buf, sizeof(tidrange_buf),
205 "%d tids (%u, %u) - (%u, %u)",
206 nlist,
211 else
212 snprintf(tidrange_buf, sizeof(tidrange_buf), "0 tids");
213
214 if (stack->parentblk != InvalidBlockNumber)
215 elog(DEBUG3, "blk %u: parent %u highkey (%u, %u), %s",
216 stack->blkno,
217 stack->parentblk,
220 tidrange_buf);
221 else
222 elog(DEBUG3, "blk %u: root leaf, %s",
223 stack->blkno,
224 tidrange_buf);
225
226 if (stack->parentblk != InvalidBlockNumber &&
228 nlist > 0 && ItemPointerCompare(&stack->parentkey, &list[nlist - 1]) < 0)
230 (errcode(ERRCODE_INDEX_CORRUPTED),
231 errmsg("index \"%s\": tid exceeds parent's high key in postingTree leaf on block %u",
232 RelationGetRelationName(rel), stack->blkno)));
233 }
234 else
235 {
236 LocationIndex pd_lower;
237 ItemPointerData bound;
238 int lowersize;
239
240 /*
241 * Check that tuples in each page are properly ordered and
242 * consistent with parent high key
243 */
244 maxoff = GinPageGetOpaque(page)->maxoff;
245 rightlink = GinPageGetOpaque(page)->rightlink;
246
247 elog(DEBUG1, "page blk: %u, type data, maxoff %d", stack->blkno, maxoff);
248
249 if (stack->parentblk != InvalidBlockNumber)
250 elog(DEBUG3, "blk %u: internal posting tree page with %u items, parent %u highkey (%u, %u)",
251 stack->blkno, maxoff, stack->parentblk,
254 else
255 elog(DEBUG3, "blk %u: root internal posting tree page with %u items",
256 stack->blkno, maxoff);
257
258 /*
259 * A GIN posting tree internal page stores PostingItems in the
260 * 'lower' part of the page. The 'upper' part is unused. The
261 * number of elements is stored in the opaque area (maxoff). Make
262 * sure the size of the 'lower' part agrees with 'maxoff'
263 *
264 * We didn't set pd_lower until PostgreSQL version 9.4, so if this
265 * check fails, it could also be because the index was
266 * binary-upgraded from an earlier version. That was a long time
267 * ago, though, so let's warn if it doesn't match.
268 */
269 pd_lower = ((PageHeader) page)->pd_lower;
270 lowersize = pd_lower - MAXALIGN(SizeOfPageHeaderData);
271 if ((lowersize - MAXALIGN(sizeof(ItemPointerData))) / sizeof(PostingItem) != maxoff)
273 (errcode(ERRCODE_INDEX_CORRUPTED),
274 errmsg("index \"%s\" has unexpected pd_lower %u in posting tree block %u with maxoff %u)",
275 RelationGetRelationName(rel), pd_lower, stack->blkno, maxoff)));
276
277 /*
278 * Before the PostingItems, there's one ItemPointerData in the
279 * 'lower' part that stores the page's high key.
280 */
281 bound = *GinDataPageGetRightBound(page);
282
283 /*
284 * Gin page right bound has a sane value only when not a highkey
285 * on the rightmost page (at a given level). For the rightmost
286 * page does not store the highkey explicitly, and the value is
287 * infinity.
288 */
289 if (ItemPointerIsValid(&stack->parentkey) &&
290 rightlink != InvalidBlockNumber &&
291 !ItemPointerEquals(&stack->parentkey, &bound))
293 (errcode(ERRCODE_INDEX_CORRUPTED),
294 errmsg("index \"%s\": posting tree page's high key (%u, %u) doesn't match the downlink on block %u (parent blk %u, key (%u, %u))",
298 stack->blkno, stack->parentblk,
301
302 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
303 {
305 PostingItem *posting_item = GinDataPageGetPostingItem(page, i);
306
307 /* ItemPointerGetOffsetNumber expects a valid pointer */
308 if (!(i == maxoff &&
309 rightlink == InvalidBlockNumber))
310 elog(DEBUG3, "key (%u, %u) -> %u",
311 ItemPointerGetBlockNumber(&posting_item->key),
312 ItemPointerGetOffsetNumber(&posting_item->key),
313 BlockIdGetBlockNumber(&posting_item->child_blkno));
314 else
315 elog(DEBUG3, "key (%u, %u) -> %u",
316 0, 0, BlockIdGetBlockNumber(&posting_item->child_blkno));
317
318 if (i == maxoff && rightlink == InvalidBlockNumber)
319 {
320 /*
321 * The rightmost item in the tree level has (0, 0) as the
322 * key
323 */
324 if (ItemPointerGetBlockNumberNoCheck(&posting_item->key) != 0 ||
325 ItemPointerGetOffsetNumberNoCheck(&posting_item->key) != 0)
327 (errcode(ERRCODE_INDEX_CORRUPTED),
328 errmsg("index \"%s\": rightmost posting tree page (blk %u) has unexpected last key (%u, %u)",
330 stack->blkno,
332 ItemPointerGetOffsetNumberNoCheck(&posting_item->key))));
333 }
334 else if (i != FirstOffsetNumber)
335 {
336 PostingItem *previous_posting_item = GinDataPageGetPostingItem(page, i - 1);
337
338 if (ItemPointerCompare(&posting_item->key, &previous_posting_item->key) < 0)
340 (errcode(ERRCODE_INDEX_CORRUPTED),
341 errmsg("index \"%s\" has wrong tuple order in posting tree, block %u, offset %u",
342 RelationGetRelationName(rel), stack->blkno, i)));
343 }
344
345 /*
346 * Check if this tuple is consistent with the downlink in the
347 * parent.
348 */
349 if (stack->parentblk != InvalidBlockNumber && i == maxoff &&
350 ItemPointerCompare(&stack->parentkey, &posting_item->key) < 0)
352 (errcode(ERRCODE_INDEX_CORRUPTED),
353 errmsg("index \"%s\": posting item exceeds parent's high key in postingTree internal page on block %u offset %u",
355 stack->blkno, i)));
356
357 /* This is an internal page, recurse into the child. */
359 ptr->depth = stack->depth + 1;
360
361 /*
362 * Set rightmost parent key to invalid iterm pointer. Its
363 * value is 'Infinity' and not explicitly stored.
364 */
365 if (rightlink == InvalidBlockNumber)
367 else
368 ptr->parentkey = posting_item->key;
369
370 ptr->parentblk = stack->blkno;
371 ptr->blkno = BlockIdGetBlockNumber(&posting_item->child_blkno);
372 ptr->next = stack->next;
373 stack->next = ptr;
374 }
375 }
376 LockBuffer(buffer, GIN_UNLOCK);
377 ReleaseBuffer(buffer);
378
379 /* Step to next item in the queue */
380 stack_next = stack->next;
381 pfree(stack);
382 stack = stack_next;
383 }
384
385 MemoryContextSwitchTo(oldcontext);
387}
static BlockNumber BlockIdGetBlockNumber(const BlockIdData *blockId)
Definition: block.h:103
PageHeaderData * PageHeader
Definition: bufpage.h:174
#define SizeOfPageHeaderData
Definition: bufpage.h:217
uint16 LocationIndex
Definition: bufpage.h:91
#define DEBUG1
Definition: elog.h:30
#define GinDataPageGetRightBound(page)
Definition: ginblock.h:288
#define GinPageIsData(page)
Definition: ginblock.h:115
#define ItemPointerSetMin(p)
Definition: ginblock.h:166
#define GinDataPageGetPostingItem(page, i)
Definition: ginblock.h:298
ItemPointer GinDataLeafPageGetItems(Page page, int *nitems, ItemPointerData advancePast)
Definition: gindatapage.c:135
Assert(PointerIsAligned(start, uint64))
int32 ItemPointerCompare(ItemPointer arg1, ItemPointer arg2)
Definition: itemptr.c:51
bool ItemPointerEquals(ItemPointer pointer1, ItemPointer pointer2)
Definition: itemptr.c:35
static void ItemPointerSetInvalid(ItemPointerData *pointer)
Definition: itemptr.h:184
static OffsetNumber ItemPointerGetOffsetNumberNoCheck(const ItemPointerData *pointer)
Definition: itemptr.h:114
static BlockNumber ItemPointerGetBlockNumber(const ItemPointerData *pointer)
Definition: itemptr.h:103
static BlockNumber ItemPointerGetBlockNumberNoCheck(const ItemPointerData *pointer)
Definition: itemptr.h:93
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition: itemptr.h:83
#define MAXPGPATH
#define snprintf
Definition: port.h:239
BlockNumber parentblk
Definition: verify_gin.c:53
ItemPointerData parentkey
Definition: verify_gin.c:52
struct GinPostingTreeScanItem * next
Definition: verify_gin.c:55
ItemPointerData key
Definition: ginblock.h:186
BlockIdData child_blkno
Definition: ginblock.h:185

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert(), BAS_BULKREAD, GinPostingTreeScanItem::blkno, BlockIdGetBlockNumber(), BufferGetPage(), CHECK_FOR_INTERRUPTS, PostingItem::child_blkno, CurrentMemoryContext, DEBUG1, DEBUG3, GinPostingTreeScanItem::depth, elog, ereport, errcode(), errmsg(), ERROR, FirstOffsetNumber, GetAccessStrategy(), GIN_SHARE, GIN_UNLOCK, GinDataLeafPageGetItems(), GinDataPageGetPostingItem, GinDataPageGetRightBound, GinPageGetOpaque, GinPageIsData, GinPageIsLeaf, i, InvalidBlockNumber, InvalidOffsetNumber, ItemPointerCompare(), ItemPointerEquals(), ItemPointerGetBlockNumber(), ItemPointerGetBlockNumberNoCheck(), ItemPointerGetOffsetNumber(), ItemPointerGetOffsetNumberNoCheck(), ItemPointerIsValid(), ItemPointerSetInvalid(), ItemPointerSetMin, PostingItem::key, sort-test::list, LockBuffer(), MAIN_FORKNUM, MAXALIGN, MAXPGPATH, MemoryContextDelete(), MemoryContextSwitchTo(), GinPostingTreeScanItem::next, OffsetNumberNext, palloc(), palloc0(), GinPostingTreeScanItem::parentblk, GinPostingTreeScanItem::parentkey, pfree(), RBM_NORMAL, ReadBufferExtended(), RelationGetRelationName, ReleaseBuffer(), SizeOfPageHeaderData, and snprintf.

Referenced by gin_check_parent_keys_consistency().

◆ gin_index_check()

Datum gin_index_check ( PG_FUNCTION_ARGS  )

Definition at line 80 of file verify_gin.c.

81{
82 Oid indrelid = PG_GETARG_OID(0);
83
85 GIN_AM_OID,
88 NULL);
89
91}
#define PG_RETURN_VOID()
Definition: fmgr.h:349
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define AccessShareLock
Definition: lockdefs.h:36
unsigned int Oid
Definition: postgres_ext.h:30
void amcheck_lock_relation_and_check(Oid indrelid, Oid am_id, IndexDoCheckCallback check, LOCKMODE lockmode, void *state)
Definition: verify_common.c:60
static void gin_check_parent_keys_consistency(Relation rel, Relation heaprel, void *callback_state, bool readonly)
Definition: verify_gin.c:395

References AccessShareLock, amcheck_lock_relation_and_check(), gin_check_parent_keys_consistency(), PG_GETARG_OID, and PG_RETURN_VOID.

◆ gin_refind_parent()

static IndexTuple gin_refind_parent ( Relation  rel,
BlockNumber  parentblkno,
BlockNumber  childblkno,
BufferAccessStrategy  strategy 
)
static

Definition at line 725 of file verify_gin.c.

727{
728 Buffer parentbuf;
729 Page parentpage;
730 OffsetNumber o,
731 parent_maxoff;
732 IndexTuple result = NULL;
733
734 parentbuf = ReadBufferExtended(rel, MAIN_FORKNUM, parentblkno, RBM_NORMAL,
735 strategy);
736
737 LockBuffer(parentbuf, GIN_SHARE);
738 parentpage = BufferGetPage(parentbuf);
739
740 if (GinPageIsLeaf(parentpage))
741 {
742 UnlockReleaseBuffer(parentbuf);
743 return result;
744 }
745
746 parent_maxoff = PageGetMaxOffsetNumber(parentpage);
747 for (o = FirstOffsetNumber; o <= parent_maxoff; o = OffsetNumberNext(o))
748 {
749 ItemId p_iid = PageGetItemIdCareful(rel, parentblkno, parentpage, o);
750 IndexTuple itup = (IndexTuple) PageGetItem(parentpage, p_iid);
751
752 if (ItemPointerGetBlockNumber(&(itup->t_tid)) == childblkno)
753 {
754 /* Found it! Make copy and return it */
755 result = CopyIndexTuple(itup);
756 break;
757 }
758 }
759
760 UnlockReleaseBuffer(parentbuf);
761
762 return result;
763}
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:5390
ItemPointerData t_tid
Definition: itup.h:37

References BufferGetPage(), CopyIndexTuple(), FirstOffsetNumber, GIN_SHARE, GinPageIsLeaf, ItemPointerGetBlockNumber(), LockBuffer(), MAIN_FORKNUM, OffsetNumberNext, PageGetItem(), PageGetItemIdCareful(), PageGetMaxOffsetNumber(), RBM_NORMAL, ReadBufferExtended(), IndexTupleData::t_tid, and UnlockReleaseBuffer().

Referenced by gin_check_parent_keys_consistency().

◆ ginReadTupleWithoutState()

static ItemPointer ginReadTupleWithoutState ( IndexTuple  itup,
int *  nitems 
)
static

Definition at line 100 of file verify_gin.c.

101{
102 Pointer ptr = GinGetPosting(itup);
103 int nipd = GinGetNPosting(itup);
104 ItemPointer ipd;
105 int ndecoded;
106
107 if (GinItupIsCompressed(itup))
108 {
109 if (nipd > 0)
110 {
111 ipd = ginPostingListDecode((GinPostingList *) ptr, &ndecoded);
112 if (nipd != ndecoded)
113 elog(ERROR, "number of items mismatch in GIN entry tuple, %d in tuple header, %d decoded",
114 nipd, ndecoded);
115 }
116 else
117 ipd = palloc(0);
118 }
119 else
120 {
121 ipd = (ItemPointer) palloc(sizeof(ItemPointerData) * nipd);
122 memcpy(ipd, ptr, sizeof(ItemPointerData) * nipd);
123 }
124 *nitems = nipd;
125 return ipd;
126}
char * Pointer
Definition: c.h:493
#define GinGetPosting(itup)
Definition: ginblock.h:238
#define GinItupIsCompressed(itup)
Definition: ginblock.h:239
#define GinGetNPosting(itup)
Definition: ginblock.h:228
ItemPointer ginPostingListDecode(GinPostingList *plist, int *ndecoded_out)
#define nitems(x)
Definition: indent.h:31
ItemPointerData * ItemPointer
Definition: itemptr.h:49

References elog, ERROR, GinGetNPosting, GinGetPosting, GinItupIsCompressed, ginPostingListDecode(), nitems, and palloc().

Referenced by gin_check_parent_keys_consistency().

◆ PageGetItemIdCareful()

static ItemId PageGetItemIdCareful ( Relation  rel,
BlockNumber  block,
Page  page,
OffsetNumber  offset 
)
static

Definition at line 766 of file verify_gin.c.

768{
769 ItemId itemid = PageGetItemId(page, offset);
770
771 if (ItemIdGetOffset(itemid) + ItemIdGetLength(itemid) >
772 BLCKSZ - MAXALIGN(sizeof(GinPageOpaqueData)))
774 (errcode(ERRCODE_INDEX_CORRUPTED),
775 errmsg("line pointer points past end of tuple space in index \"%s\"",
777 errdetail_internal("Index tid=(%u,%u) lp_off=%u, lp_len=%u lp_flags=%u.",
778 block, offset, ItemIdGetOffset(itemid),
779 ItemIdGetLength(itemid),
780 ItemIdGetFlags(itemid))));
781
782 /*
783 * Verify that line pointer isn't LP_REDIRECT or LP_UNUSED or LP_DEAD,
784 * since GIN never uses all three. Verify that line pointer has storage,
785 * too.
786 */
787 if (ItemIdIsRedirected(itemid) || !ItemIdIsUsed(itemid) ||
788 ItemIdIsDead(itemid) || ItemIdGetLength(itemid) == 0)
790 (errcode(ERRCODE_INDEX_CORRUPTED),
791 errmsg("invalid line pointer storage in index \"%s\"",
793 errdetail_internal("Index tid=(%u,%u) lp_off=%u, lp_len=%u lp_flags=%u.",
794 block, offset, ItemIdGetOffset(itemid),
795 ItemIdGetLength(itemid),
796 ItemIdGetFlags(itemid))));
797
798 return itemid;
799}
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:244
int errdetail_internal(const char *fmt,...)
Definition: elog.c:1231
#define ItemIdGetOffset(itemId)
Definition: itemid.h:65
#define ItemIdIsDead(itemId)
Definition: itemid.h:113
#define ItemIdIsUsed(itemId)
Definition: itemid.h:92
#define ItemIdIsRedirected(itemId)
Definition: itemid.h:106
#define ItemIdGetFlags(itemId)
Definition: itemid.h:71

References ereport, errcode(), errdetail_internal(), errmsg(), ERROR, ItemIdGetFlags, ItemIdGetLength, ItemIdGetOffset, ItemIdIsDead, ItemIdIsRedirected, ItemIdIsUsed, MAXALIGN, PageGetItemId(), and RelationGetRelationName.

Referenced by gin_check_parent_keys_consistency(), and gin_refind_parent().

◆ PG_FUNCTION_INFO_V1()

PG_FUNCTION_INFO_V1 ( gin_index_check  )