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 664 of file verify_gin.c.

665{
666 Page page = BufferGetPage(buffer);
667
668 /*
669 * ReadBuffer verifies that every newly-read page passes
670 * PageHeaderIsValid, which means it either contains a reasonably sane
671 * page header or is all-zero. We have to defend against the all-zero
672 * case, however.
673 */
674 if (PageIsNew(page))
676 (errcode(ERRCODE_INDEX_CORRUPTED),
677 errmsg("index \"%s\" contains unexpected zero page at block %u",
679 BufferGetBlockNumber(buffer)),
680 errhint("Please REINDEX it.")));
681
682 /*
683 * Additionally check that the special area looks sane.
684 */
685 if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData)))
687 (errcode(ERRCODE_INDEX_CORRUPTED),
688 errmsg("index \"%s\" contains corrupted page at block %u",
690 BufferGetBlockNumber(buffer)),
691 errhint("Please REINDEX it.")));
692
693 if (GinPageIsDeleted(page))
694 {
695 if (!GinPageIsLeaf(page))
697 (errcode(ERRCODE_INDEX_CORRUPTED),
698 errmsg("index \"%s\" has deleted internal page %u",
699 RelationGetRelationName(rel), blockNo)));
702 (errcode(ERRCODE_INDEX_CORRUPTED),
703 errmsg("index \"%s\" has deleted page %u with tuples",
704 RelationGetRelationName(rel), blockNo)));
705 }
708 (errcode(ERRCODE_INDEX_CORRUPTED),
709 errmsg("index \"%s\" has page %u with exceeding count of tuples",
710 RelationGetRelationName(rel), blockNo)));
711}
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:4198
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:810
int errhint(const char *fmt,...)
Definition: elog.c:1321
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:150
#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:548

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 390 of file verify_gin.c.

394{
396 GinScanItem *stack;
397 MemoryContext mctx;
398 MemoryContext oldcontext;
400 int leafdepth;
401
403 "amcheck consistency check context",
405 oldcontext = MemoryContextSwitchTo(mctx);
406 initGinState(&state, rel);
407
408 /*
409 * We don't know the height of the tree yet, but as soon as we encounter a
410 * leaf page, we will set 'leafdepth' to its depth.
411 */
412 leafdepth = -1;
413
414 /* Start the scan at the root page */
415 stack = (GinScanItem *) palloc0(sizeof(GinScanItem));
416 stack->depth = 0;
417 stack->parenttup = NULL;
419 stack->blkno = GIN_ROOT_BLKNO;
420
421 while (stack)
422 {
423 GinScanItem *stack_next;
424 Buffer buffer;
425 Page page;
427 maxoff,
428 prev_attnum;
429 IndexTuple prev_tuple;
430 BlockNumber rightlink;
431
433
434 buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno,
435 RBM_NORMAL, strategy);
436 LockBuffer(buffer, GIN_SHARE);
437 page = BufferGetPage(buffer);
438 maxoff = PageGetMaxOffsetNumber(page);
439 rightlink = GinPageGetOpaque(page)->rightlink;
440
441 /* Do basic sanity checks on the page headers */
442 check_index_page(rel, buffer, stack->blkno);
443
444 elog(DEBUG3, "processing entry tree page at blk %u, maxoff: %u", stack->blkno, maxoff);
445
446 /*
447 * It's possible that the page was split since we looked at the
448 * parent, so that we didn't missed the downlink of the right sibling
449 * when we scanned the parent. If so, add the right sibling to the
450 * stack now.
451 */
452 if (stack->parenttup != NULL)
453 {
454 GinNullCategory parent_key_category;
455 Datum parent_key = gintuple_get_key(&state,
456 stack->parenttup,
457 &parent_key_category);
458 OffsetNumber parent_key_attnum = gintuple_get_attrnum(&state, stack->parenttup);
459 ItemId iid = PageGetItemIdCareful(rel, stack->blkno,
460 page, maxoff);
461 IndexTuple idxtuple = (IndexTuple) PageGetItem(page, iid);
462 OffsetNumber page_max_key_attnum = gintuple_get_attrnum(&state, idxtuple);
463 GinNullCategory page_max_key_category;
464 Datum page_max_key = gintuple_get_key(&state, idxtuple, &page_max_key_category);
465
466 if (rightlink != InvalidBlockNumber &&
467 ginCompareAttEntries(&state, page_max_key_attnum, page_max_key,
468 page_max_key_category, parent_key_attnum,
469 parent_key, parent_key_category) < 0)
470 {
471 /* split page detected, install right link to the stack */
472 GinScanItem *ptr;
473
474 elog(DEBUG3, "split detected for blk: %u, parent blk: %u", stack->blkno, stack->parentblk);
475
476 ptr = (GinScanItem *) palloc(sizeof(GinScanItem));
477 ptr->depth = stack->depth;
478 ptr->parenttup = CopyIndexTuple(stack->parenttup);
479 ptr->parentblk = stack->parentblk;
480 ptr->blkno = rightlink;
481 ptr->next = stack->next;
482 stack->next = ptr;
483 }
484 }
485
486 /* Check that the tree has the same height in all branches */
487 if (GinPageIsLeaf(page))
488 {
489 if (leafdepth == -1)
490 leafdepth = stack->depth;
491 else if (stack->depth != leafdepth)
493 (errcode(ERRCODE_INDEX_CORRUPTED),
494 errmsg("index \"%s\": internal pages traversal encountered leaf page unexpectedly on block %u",
495 RelationGetRelationName(rel), stack->blkno)));
496 }
497
498 /*
499 * Check that tuples in each page are properly ordered and consistent
500 * with parent high key
501 */
502 prev_tuple = NULL;
503 prev_attnum = InvalidAttrNumber;
504 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
505 {
506 ItemId iid = PageGetItemIdCareful(rel, stack->blkno, page, i);
507 IndexTuple idxtuple = (IndexTuple) PageGetItem(page, iid);
508 OffsetNumber current_attnum = gintuple_get_attrnum(&state, idxtuple);
509 GinNullCategory current_key_category;
510 Datum current_key;
511
512 if (MAXALIGN(ItemIdGetLength(iid)) != MAXALIGN(IndexTupleSize(idxtuple)))
514 (errcode(ERRCODE_INDEX_CORRUPTED),
515 errmsg("index \"%s\" has inconsistent tuple sizes, block %u, offset %u",
516 RelationGetRelationName(rel), stack->blkno, i)));
517
518 current_key = gintuple_get_key(&state, idxtuple, &current_key_category);
519
520 /*
521 * Compare the entry to the preceding one.
522 *
523 * Don't check for high key on the rightmost inner page, as this
524 * key is not really stored explicitly.
525 *
526 * The entries may be for different attributes, so make sure to
527 * use ginCompareAttEntries for comparison.
528 */
529 if ((i != FirstOffsetNumber) &&
530 !(i == maxoff && rightlink == InvalidBlockNumber && !GinPageIsLeaf(page)))
531 {
532 Datum prev_key;
533 GinNullCategory prev_key_category;
534
535 prev_key = gintuple_get_key(&state, prev_tuple, &prev_key_category);
536 if (ginCompareAttEntries(&state, prev_attnum, prev_key,
537 prev_key_category, current_attnum,
538 current_key, current_key_category) >= 0)
540 (errcode(ERRCODE_INDEX_CORRUPTED),
541 errmsg("index \"%s\" has wrong tuple order on entry tree page, block %u, offset %u, rightlink %u",
542 RelationGetRelationName(rel), stack->blkno, i, rightlink)));
543 }
544
545 /*
546 * Check if this tuple is consistent with the downlink in the
547 * parent.
548 */
549 if (stack->parenttup &&
550 i == maxoff)
551 {
552 GinNullCategory parent_key_category;
553 OffsetNumber parent_key_attnum = gintuple_get_attrnum(&state, stack->parenttup);
554 Datum parent_key = gintuple_get_key(&state,
555 stack->parenttup,
556 &parent_key_category);
557
558 if (ginCompareAttEntries(&state, current_attnum, current_key,
559 current_key_category, parent_key_attnum,
560 parent_key, parent_key_category) > 0)
561 {
562 /*
563 * There was a discrepancy between parent and child
564 * tuples. We need to verify it is not a result of
565 * concurrent call of gistplacetopage(). So, lock parent
566 * and try to find downlink for current page. It may be
567 * missing due to concurrent page split, this is OK.
568 */
569 pfree(stack->parenttup);
570 stack->parenttup = gin_refind_parent(rel, stack->parentblk,
571 stack->blkno, strategy);
572
573 /* We found it - make a final check before failing */
574 if (!stack->parenttup)
575 elog(NOTICE, "Unable to find parent tuple for block %u on block %u due to concurrent split",
576 stack->blkno, stack->parentblk);
577 else
578 {
579 parent_key_attnum = gintuple_get_attrnum(&state, stack->parenttup);
580 parent_key = gintuple_get_key(&state,
581 stack->parenttup,
582 &parent_key_category);
583
584 /*
585 * Check if it is properly adjusted. If succeed,
586 * proceed to the next key.
587 */
588 if (ginCompareAttEntries(&state, current_attnum, current_key,
589 current_key_category, parent_key_attnum,
590 parent_key, parent_key_category) > 0)
592 (errcode(ERRCODE_INDEX_CORRUPTED),
593 errmsg("index \"%s\" has inconsistent records on page %u offset %u",
594 RelationGetRelationName(rel), stack->blkno, i)));
595 }
596 }
597 }
598
599 /* If this is an internal page, recurse into the child */
600 if (!GinPageIsLeaf(page))
601 {
602 GinScanItem *ptr;
603
604 ptr = (GinScanItem *) palloc(sizeof(GinScanItem));
605 ptr->depth = stack->depth + 1;
606 /* last tuple in layer has no high key */
607 if (i == maxoff && rightlink == InvalidBlockNumber)
608 ptr->parenttup = NULL;
609 else
610 ptr->parenttup = CopyIndexTuple(idxtuple);
611 ptr->parentblk = stack->blkno;
612 ptr->blkno = GinGetDownlink(idxtuple);
613 ptr->next = stack->next;
614 stack->next = ptr;
615 }
616 /* If this item is a pointer to a posting tree, recurse into it */
617 else if (GinIsPostingTree(idxtuple))
618 {
619 BlockNumber rootPostingTree = GinGetPostingTree(idxtuple);
620
622 }
623 else
624 {
625 ItemPointer ipd;
626 int nipd;
627
628 ipd = ginReadTupleWithoutState(idxtuple, &nipd);
629
630 for (int j = 0; j < nipd; j++)
631 {
634 (errcode(ERRCODE_INDEX_CORRUPTED),
635 errmsg("index \"%s\": posting list contains invalid heap pointer on block %u",
636 RelationGetRelationName(rel), stack->blkno)));
637 }
638 pfree(ipd);
639 }
640
641 prev_tuple = CopyIndexTuple(idxtuple);
642 prev_attnum = current_attnum;
643 }
644
645 LockBuffer(buffer, GIN_UNLOCK);
646 ReleaseBuffer(buffer);
647
648 /* Step to next item in the queue */
649 stack_next = stack->next;
650 if (stack->parenttup)
651 pfree(stack->parenttup);
652 pfree(stack);
653 stack = stack_next;
654 }
655
656 MemoryContextSwitchTo(oldcontext);
658}
#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:5338
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:5572
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:424
#define GIN_UNLOCK
Definition: gin_private.h:50
#define GIN_SHARE
Definition: gin_private.h:51
#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
int ginCompareAttEntries(GinState *ginstate, OffsetNumber attnuma, Datum a, GinNullCategory categorya, OffsetNumber attnumb, Datum b, GinNullCategory categoryb)
Definition: ginutil.c:415
Datum gintuple_get_key(GinState *ginstate, IndexTuple tuple, GinNullCategory *category)
Definition: ginutil.c:264
void initGinState(GinState *state, Relation index)
Definition: ginutil.c:102
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:1594
void * palloc0(Size size)
Definition: mcxt.c:1395
void * palloc(Size size)
Definition: mcxt.c:1365
MemoryContext CurrentMemoryContext
Definition: mcxt.c:160
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:469
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
#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
uint64_t Datum
Definition: postgres.h:70
@ MAIN_FORKNUM
Definition: relpath.h:58
IndexTuple parenttup
Definition: verify_gin.c:39
BlockNumber parentblk
Definition: verify_gin.c:40
BlockNumber blkno
Definition: verify_gin.c:41
struct GinScanItem * next
Definition: verify_gin.c:42
Definition: regguts.h:323
static IndexTuple gin_refind_parent(Relation rel, BlockNumber parentblkno, BlockNumber childblkno, BufferAccessStrategy strategy)
Definition: verify_gin.c:720
static ItemPointer ginReadTupleWithoutState(IndexTuple itup, int *nitems)
Definition: verify_gin.c:99
static void gin_check_posting_tree_parent_keys_consistency(Relation rel, BlockNumber posting_tree_root)
Definition: verify_gin.c:134
static void check_index_page(Relation rel, Buffer buffer, BlockNumber blockNo)
Definition: verify_gin.c:664
static ItemId PageGetItemIdCareful(Relation rel, BlockNumber block, Page page, OffsetNumber offset)
Definition: verify_gin.c:761

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, BAS_BULKREAD, GinScanItem::blkno, 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, ginCompareAttEntries(), GinGetDownlink, GinGetPostingTree, GinIsPostingTree, GinPageGetOpaque, GinPageIsLeaf, ginReadTupleWithoutState(), gintuple_get_attrnum(), gintuple_get_key(), i, IndexTupleSize(), initGinState(), InvalidAttrNumber, InvalidBlockNumber, ItemIdGetLength, ItemPointerGetOffsetNumber(), j, LockBuffer(), MAIN_FORKNUM, MAXALIGN, MemoryContextDelete(), MemoryContextSwitchTo(), GinScanItem::next, NOTICE, OffsetNumberIsValid, OffsetNumberNext, PageGetItem(), PageGetItemIdCareful(), PageGetMaxOffsetNumber(), palloc(), palloc0(), GinScanItem::parentblk, 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 134 of file verify_gin.c.

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

80{
81 Oid indrelid = PG_GETARG_OID(0);
82
84 GIN_AM_OID,
87 NULL);
88
90}
#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:32
void amcheck_lock_relation_and_check(Oid indrelid, Oid am_id, IndexDoCheckCallback check, LOCKMODE lockmode, void *state)
Definition: verify_common.c:62
static void gin_check_parent_keys_consistency(Relation rel, Relation heaprel, void *callback_state, bool readonly)
Definition: verify_gin.c:390

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 720 of file verify_gin.c.

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

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

Referenced by gin_check_parent_keys_consistency().

◆ ginReadTupleWithoutState()

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

Definition at line 99 of file verify_gin.c.

100{
101 Pointer ptr = GinGetPosting(itup);
102 int nipd = GinGetNPosting(itup);
103 ItemPointer ipd;
104 int ndecoded;
105
106 if (GinItupIsCompressed(itup))
107 {
108 if (nipd > 0)
109 {
110 ipd = ginPostingListDecode((GinPostingList *) ptr, &ndecoded);
111 if (nipd != ndecoded)
112 elog(ERROR, "number of items mismatch in GIN entry tuple, %d in tuple header, %d decoded",
113 nipd, ndecoded);
114 }
115 else
116 ipd = palloc(0);
117 }
118 else
119 {
120 ipd = (ItemPointer) palloc(sizeof(ItemPointerData) * nipd);
121 memcpy(ipd, ptr, sizeof(ItemPointerData) * nipd);
122 }
123 *nitems = nipd;
124 return ipd;
125}
char * Pointer
Definition: c.h:529
#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 761 of file verify_gin.c.

763{
764 ItemId itemid = PageGetItemId(page, offset);
765
766 if (ItemIdGetOffset(itemid) + ItemIdGetLength(itemid) >
767 BLCKSZ - MAXALIGN(sizeof(GinPageOpaqueData)))
769 (errcode(ERRCODE_INDEX_CORRUPTED),
770 errmsg("line pointer points past end of tuple space in index \"%s\"",
772 errdetail_internal("Index tid=(%u,%u) lp_off=%u, lp_len=%u lp_flags=%u.",
773 block, offset, ItemIdGetOffset(itemid),
774 ItemIdGetLength(itemid),
775 ItemIdGetFlags(itemid))));
776
777 /*
778 * Verify that line pointer isn't LP_REDIRECT or LP_UNUSED or LP_DEAD,
779 * since GIN never uses all three. Verify that line pointer has storage,
780 * too.
781 */
782 if (ItemIdIsRedirected(itemid) || !ItemIdIsUsed(itemid) ||
783 ItemIdIsDead(itemid) || ItemIdGetLength(itemid) == 0)
785 (errcode(ERRCODE_INDEX_CORRUPTED),
786 errmsg("invalid line pointer storage in index \"%s\"",
788 errdetail_internal("Index tid=(%u,%u) lp_off=%u, lp_len=%u lp_flags=%u.",
789 block, offset, ItemIdGetOffset(itemid),
790 ItemIdGetLength(itemid),
791 ItemIdGetFlags(itemid))));
792
793 return itemid;
794}
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:244
int errdetail_internal(const char *fmt,...)
Definition: elog.c:1234
#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  )