PostgreSQL Source Code git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
verify_gin.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * verify_gin.c
4 * Verifies the integrity of GIN indexes based on invariants.
5 *
6 *
7 * GIN index verification checks a number of invariants:
8 *
9 * - consistency: Paths in GIN graph have to contain consistent keys: tuples
10 * on parent pages consistently include tuples from children pages.
11 *
12 * - graph invariants: Each internal page must have at least one downlink, and
13 * can reference either only leaf pages or only internal pages.
14 *
15 *
16 * Copyright (c) 2016-2025, PostgreSQL Global Development Group
17 *
18 * IDENTIFICATION
19 * contrib/amcheck/verify_gin.c
20 *
21 *-------------------------------------------------------------------------
22 */
23#include "postgres.h"
24
25#include "access/gin_private.h"
26#include "access/nbtree.h"
27#include "catalog/pg_am.h"
28#include "utils/memutils.h"
29#include "utils/rel.h"
30#include "verify_common.h"
31#include "string.h"
32
33/*
34 * GinScanItem represents one item of depth-first scan of the index.
35 */
36typedef struct GinScanItem
37{
38 int depth;
45
46/*
47 * GinPostingTreeScanItem represents one item of a depth-first posting tree scan.
48 */
50{
51 int depth;
57
58
60
62 Relation heaprel,
63 void *callback_state, bool readonly);
64static void check_index_page(Relation rel, Buffer buffer, BlockNumber blockNo);
66 BlockNumber parentblkno,
67 BlockNumber childblkno,
68 BufferAccessStrategy strategy);
70 OffsetNumber offset);
71
72/*
73 * gin_index_check(index regclass)
74 *
75 * Verify integrity of GIN index.
76 *
77 * Acquires AccessShareLock on heap & index relations.
78 */
81{
82 Oid indrelid = PG_GETARG_OID(0);
83
85 GIN_AM_OID,
88 NULL);
89
91}
92
93/*
94 * Read item pointers from leaf entry tuple.
95 *
96 * Returns a palloc'd array of ItemPointers. The number of items is returned
97 * in *nitems.
98 */
99static ItemPointer
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}
127
128/*
129 * Scans through a posting tree (given by the root), and verifies that the keys
130 * on a child keys are consistent with the parent.
131 *
132 * Allocates a separate memory context and scans through posting tree graph.
133 */
134static void
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}
388
389/*
390 * Main entry point for GIN checks.
391 *
392 * Allocates memory context and scans through the whole GIN graph.
393 */
394static void
396 Relation heaprel,
397 void *callback_state,
398 bool readonly)
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}
664
665/*
666 * Verify that a freshly-read page looks sane.
667 */
668static void
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 %d",
704 RelationGetRelationName(rel), blockNo)));
707 (errcode(ERRCODE_INDEX_CORRUPTED),
708 errmsg("index \"%s\" has deleted page %d with tuples",
709 RelationGetRelationName(rel), blockNo)));
710 }
713 (errcode(ERRCODE_INDEX_CORRUPTED),
714 errmsg("index \"%s\" has page %d with exceeding count of tuples",
715 RelationGetRelationName(rel), blockNo)));
716}
717
718/*
719 * Try to re-find downlink pointing to 'blkno', in 'parentblkno'.
720 *
721 * If found, returns a palloc'd copy of the downlink tuple. Otherwise,
722 * returns NULL.
723 */
724static IndexTuple
726 BlockNumber childblkno, BufferAccessStrategy strategy)
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}
764
765static ItemId
767 OffsetNumber offset)
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}
#define InvalidAttrNumber
Definition: attnum.h:23
uint32 BlockNumber
Definition: block.h:31
#define InvalidBlockNumber
Definition: block.h:33
static BlockNumber BlockIdGetBlockNumber(const BlockIdData *blockId)
Definition: block.h:103
int Buffer
Definition: buf.h:23
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:4161
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:5303
XLogRecPtr BufferGetLSNAtomic(Buffer buffer)
Definition: bufmgr.c:4423
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:5320
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:5537
Buffer ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum, ReadBufferMode mode, BufferAccessStrategy strategy)
Definition: bufmgr.c:798
@ BAS_BULKREAD
Definition: bufmgr.h:37
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:414
@ RBM_NORMAL
Definition: bufmgr.h:46
PageHeaderData * PageHeader
Definition: bufpage.h:174
static uint16 PageGetSpecialSize(const PageData *page)
Definition: bufpage.h:317
static Item PageGetItem(const PageData *page, const ItemIdData *itemId)
Definition: bufpage.h:354
static bool PageIsNew(const PageData *page)
Definition: bufpage.h:234
#define SizeOfPageHeaderData
Definition: bufpage.h:217
static ItemId PageGetItemId(Page page, OffsetNumber offsetNumber)
Definition: bufpage.h:244
PageData * Page
Definition: bufpage.h:82
uint16 LocationIndex
Definition: bufpage.h:91
static OffsetNumber PageGetMaxOffsetNumber(const PageData *page)
Definition: bufpage.h:372
#define MAXALIGN(LEN)
Definition: c.h:782
char * Pointer
Definition: c.h:493
int errdetail_internal(const char *fmt,...)
Definition: elog.c:1231
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 DEBUG3
Definition: elog.h:28
#define DEBUG1
Definition: elog.h:30
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:226
#define NOTICE
Definition: elog.h:35
#define ereport(elevel,...)
Definition: elog.h:149
#define PG_RETURN_VOID()
Definition: fmgr.h:349
#define PG_GETARG_OID(n)
Definition: fmgr.h:275
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
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 GinGetPosting(itup)
Definition: ginblock.h:238
#define GIN_ROOT_BLKNO
Definition: ginblock.h:52
#define GinGetDownlink(itup)
Definition: ginblock.h:257
#define GinItupIsCompressed(itup)
Definition: ginblock.h:239
#define GinGetNPosting(itup)
Definition: ginblock.h:228
#define GinDataPageGetRightBound(page)
Definition: ginblock.h:288
#define GinGetPostingTree(itup)
Definition: ginblock.h:233
#define GinPageIsData(page)
Definition: ginblock.h:115
signed char GinNullCategory
Definition: ginblock.h:206
#define ItemPointerSetMin(p)
Definition: ginblock.h:166
#define GinDataPageGetPostingItem(page, i)
Definition: ginblock.h:298
#define GinPageIsDeleted(page)
Definition: ginblock.h:124
#define GinPageIsLeaf(page)
Definition: ginblock.h:112
ItemPointer GinDataLeafPageGetItems(Page page, int *nitems, ItemPointerData advancePast)
Definition: gindatapage.c:135
ItemPointer ginPostingListDecode(GinPostingList *plist, int *ndecoded_out)
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
Assert(PointerIsAligned(start, uint64))
#define nitems(x)
Definition: indent.h:31
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
#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
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 ItemPointerGetOffsetNumber(const ItemPointerData *pointer)
Definition: itemptr.h:124
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
ItemPointerData * ItemPointer
Definition: itemptr.h:49
static bool ItemPointerIsValid(const ItemPointerData *pointer)
Definition: itemptr.h:83
IndexTupleData * IndexTuple
Definition: itup.h:53
static Size IndexTupleSize(const IndexTupleData *itup)
Definition: itup.h:71
#define MaxIndexTuplesPerPage
Definition: itup.h:181
#define AccessShareLock
Definition: lockdefs.h:36
void pfree(void *pointer)
Definition: mcxt.c:2147
void * palloc0(Size size)
Definition: mcxt.c:1970
void * palloc(Size size)
Definition: mcxt.c:1940
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 InvalidOffsetNumber
Definition: off.h:26
#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
#define MAXPGPATH
#define snprintf
Definition: port.h:239
uintptr_t Datum
Definition: postgres.h:69
unsigned int Oid
Definition: postgres_ext.h:30
#define RelationGetRelationName(relation)
Definition: rel.h:550
@ MAIN_FORKNUM
Definition: relpath.h:58
BlockNumber parentblk
Definition: verify_gin.c:53
ItemPointerData parentkey
Definition: verify_gin.c:52
struct GinPostingTreeScanItem * next
Definition: verify_gin.c:55
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
ItemPointerData t_tid
Definition: itup.h:37
ItemPointerData key
Definition: ginblock.h:186
BlockIdData child_blkno
Definition: ginblock.h:185
Definition: regguts.h:323
void amcheck_lock_relation_and_check(Oid indrelid, Oid am_id, IndexDoCheckCallback check, LOCKMODE lockmode, void *state)
Definition: verify_common.c:60
Datum gin_index_check(PG_FUNCTION_ARGS)
Definition: verify_gin.c:80
static IndexTuple gin_refind_parent(Relation rel, BlockNumber parentblkno, BlockNumber childblkno, BufferAccessStrategy strategy)
Definition: verify_gin.c:725
static void gin_check_parent_keys_consistency(Relation rel, Relation heaprel, void *callback_state, bool readonly)
Definition: verify_gin.c:395
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
struct GinPostingTreeScanItem GinPostingTreeScanItem
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
struct GinScanItem GinScanItem
PG_FUNCTION_INFO_V1(gin_index_check)
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28