LCOV - differential code coverage report
Current view: top level - contrib/amcheck - verify_gin.c (source / functions) Coverage Total Hit UNC UBC GNC CBC DUB DCB
Current: c70b6db34ffeab48beef1fb4ce61bcad3772b8dd vs 06473f5a344df8c9594ead90a609b86f6724cff8 Lines: 52.7 % 239 126 1 112 1 125 1 1
Current Date: 2025-09-06 07:49:51 +0900 Functions: 87.5 % 8 7 1 1 6
Baseline: lcov-20250906-005545-baseline Branches: 29.9 % 214 64 150 64
Baseline Date: 2025-09-05 08:21:35 +0100 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 50.0 % 2 1 1 1
(30,360] days: 52.7 % 237 125 112 125
Function coverage date bins:
(30,360] days: 87.5 % 8 7 1 1 6
Branch coverage date bins:
(30,360] days: 29.9 % 214 64 150 64

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  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                 :                :  */
                                 36                 :                : typedef struct GinScanItem
                                 37                 :                : {
                                 38                 :                :     int         depth;
                                 39                 :                :     IndexTuple  parenttup;
                                 40                 :                :     BlockNumber parentblk;
                                 41                 :                :     BlockNumber blkno;
                                 42                 :                :     struct GinScanItem *next;
                                 43                 :                : } GinScanItem;
                                 44                 :                : 
                                 45                 :                : /*
                                 46                 :                :  * GinPostingTreeScanItem represents one item of a depth-first posting tree scan.
                                 47                 :                :  */
                                 48                 :                : typedef struct GinPostingTreeScanItem
                                 49                 :                : {
                                 50                 :                :     int         depth;
                                 51                 :                :     ItemPointerData parentkey;
                                 52                 :                :     BlockNumber parentblk;
                                 53                 :                :     BlockNumber blkno;
                                 54                 :                :     struct GinPostingTreeScanItem *next;
                                 55                 :                : } GinPostingTreeScanItem;
                                 56                 :                : 
                                 57                 :                : 
  161 tomas.vondra@postgre       58                 :CBC          28 : PG_FUNCTION_INFO_V1(gin_index_check);
                                 59                 :                : 
                                 60                 :                : static void gin_check_parent_keys_consistency(Relation rel,
                                 61                 :                :                                               Relation heaprel,
                                 62                 :                :                                               void *callback_state, bool readonly);
                                 63                 :                : static void check_index_page(Relation rel, Buffer buffer, BlockNumber blockNo);
                                 64                 :                : static IndexTuple gin_refind_parent(Relation rel,
                                 65                 :                :                                     BlockNumber parentblkno,
                                 66                 :                :                                     BlockNumber childblkno,
                                 67                 :                :                                     BufferAccessStrategy strategy);
                                 68                 :                : static ItemId PageGetItemIdCareful(Relation rel, BlockNumber block, Page page,
                                 69                 :                :                                    OffsetNumber offset);
                                 70                 :                : 
                                 71                 :                : /*
                                 72                 :                :  * gin_index_check(index regclass)
                                 73                 :                :  *
                                 74                 :                :  * Verify integrity of GIN index.
                                 75                 :                :  *
                                 76                 :                :  * Acquires AccessShareLock on heap & index relations.
                                 77                 :                :  */
                                 78                 :                : Datum
                                 79                 :             32 : gin_index_check(PG_FUNCTION_ARGS)
                                 80                 :                : {
                                 81                 :             32 :     Oid         indrelid = PG_GETARG_OID(0);
                                 82                 :                : 
                                 83                 :             32 :     amcheck_lock_relation_and_check(indrelid,
                                 84                 :                :                                     GIN_AM_OID,
                                 85                 :                :                                     gin_check_parent_keys_consistency,
                                 86                 :                :                                     AccessShareLock,
                                 87                 :                :                                     NULL);
                                 88                 :                : 
                                 89                 :             27 :     PG_RETURN_VOID();
                                 90                 :                : }
                                 91                 :                : 
                                 92                 :                : /*
                                 93                 :                :  * Read item pointers from leaf entry tuple.
                                 94                 :                :  *
                                 95                 :                :  * Returns a palloc'd array of ItemPointers. The number of items is returned
                                 96                 :                :  * in *nitems.
                                 97                 :                :  */
                                 98                 :                : static ItemPointer
                                 99                 :           1471 : ginReadTupleWithoutState(IndexTuple itup, int *nitems)
                                100                 :                : {
                                101                 :           1471 :     Pointer     ptr = GinGetPosting(itup);
                                102                 :           1471 :     int         nipd = GinGetNPosting(itup);
                                103                 :                :     ItemPointer ipd;
                                104                 :                :     int         ndecoded;
                                105                 :                : 
                                106         [ +  - ]:           1471 :     if (GinItupIsCompressed(itup))
                                107                 :                :     {
                                108         [ +  - ]:           1471 :         if (nipd > 0)
                                109                 :                :         {
                                110                 :           1471 :             ipd = ginPostingListDecode((GinPostingList *) ptr, &ndecoded);
                                111         [ -  + ]:           1471 :             if (nipd != ndecoded)
  161 tomas.vondra@postgre      112         [ #  # ]:UBC           0 :                 elog(ERROR, "number of items mismatch in GIN entry tuple, %d in tuple header, %d decoded",
                                113                 :                :                      nipd, ndecoded);
                                114                 :                :         }
                                115                 :                :         else
                                116                 :              0 :             ipd = palloc(0);
                                117                 :                :     }
                                118                 :                :     else
                                119                 :                :     {
                                120                 :              0 :         ipd = (ItemPointer) palloc(sizeof(ItemPointerData) * nipd);
                                121                 :              0 :         memcpy(ipd, ptr, sizeof(ItemPointerData) * nipd);
                                122                 :                :     }
  161 tomas.vondra@postgre      123                 :CBC        1471 :     *nitems = nipd;
                                124                 :           1471 :     return ipd;
                                125                 :                : }
                                126                 :                : 
                                127                 :                : /*
                                128                 :                :  * Scans through a posting tree (given by the root), and verifies that the keys
                                129                 :                :  * on a child keys are consistent with the parent.
                                130                 :                :  *
                                131                 :                :  * Allocates a separate memory context and scans through posting tree graph.
                                132                 :                :  */
                                133                 :                : static void
  161 tomas.vondra@postgre      134                 :UBC           0 : gin_check_posting_tree_parent_keys_consistency(Relation rel, BlockNumber posting_tree_root)
                                135                 :                : {
                                136                 :              0 :     BufferAccessStrategy strategy = GetAccessStrategy(BAS_BULKREAD);
                                137                 :                :     GinPostingTreeScanItem *stack;
                                138                 :                :     MemoryContext mctx;
                                139                 :                :     MemoryContext oldcontext;
                                140                 :                : 
                                141                 :                :     int         leafdepth;
                                142                 :                : 
                                143                 :              0 :     mctx = AllocSetContextCreate(CurrentMemoryContext,
                                144                 :                :                                  "posting tree check context",
                                145                 :                :                                  ALLOCSET_DEFAULT_SIZES);
                                146                 :              0 :     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                 :              0 :     leafdepth = -1;
                                153                 :                : 
                                154                 :                :     /* Start the scan at the root page */
                                155                 :              0 :     stack = (GinPostingTreeScanItem *) palloc0(sizeof(GinPostingTreeScanItem));
                                156                 :              0 :     stack->depth = 0;
                                157                 :              0 :     ItemPointerSetInvalid(&stack->parentkey);
                                158                 :              0 :     stack->parentblk = InvalidBlockNumber;
                                159                 :              0 :     stack->blkno = posting_tree_root;
                                160                 :                : 
                                161         [ #  # ]:              0 :     elog(DEBUG3, "processing posting tree at blk %u", posting_tree_root);
                                162                 :                : 
                                163         [ #  # ]:              0 :     while (stack)
                                164                 :                :     {
                                165                 :                :         GinPostingTreeScanItem *stack_next;
                                166                 :                :         Buffer      buffer;
                                167                 :                :         Page        page;
                                168                 :                :         OffsetNumber i,
                                169                 :                :                     maxoff;
                                170                 :                :         BlockNumber rightlink;
                                171                 :                : 
                                172         [ #  # ]:              0 :         CHECK_FOR_INTERRUPTS();
                                173                 :                : 
                                174                 :              0 :         buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno,
                                175                 :                :                                     RBM_NORMAL, strategy);
                                176                 :              0 :         LockBuffer(buffer, GIN_SHARE);
    8 peter@eisentraut.org      177                 :UNC           0 :         page = BufferGetPage(buffer);
                                178                 :                : 
  161 tomas.vondra@postgre      179         [ #  # ]:UBC           0 :         Assert(GinPageIsData(page));
                                180                 :                : 
                                181                 :                :         /* Check that the tree has the same height in all branches */
                                182         [ #  # ]:              0 :         if (GinPageIsLeaf(page))
                                183                 :                :         {
                                184                 :                :             ItemPointerData minItem;
                                185                 :                :             int         nlist;
                                186                 :                :             ItemPointerData *list;
                                187                 :                :             char        tidrange_buf[MAXPGPATH];
                                188                 :                : 
                                189                 :              0 :             ItemPointerSetMin(&minItem);
                                190                 :                : 
                                191         [ #  # ]:              0 :             elog(DEBUG1, "page blk: %u, type leaf", stack->blkno);
                                192                 :                : 
                                193         [ #  # ]:              0 :             if (leafdepth == -1)
                                194                 :              0 :                 leafdepth = stack->depth;
                                195         [ #  # ]:              0 :             else if (stack->depth != leafdepth)
                                196         [ #  # ]:              0 :                 ereport(ERROR,
                                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                 :              0 :             list = GinDataLeafPageGetItems(page, &nlist, minItem);
                                201                 :                : 
                                202         [ #  # ]:              0 :             if (nlist > 0)
                                203                 :              0 :                 snprintf(tidrange_buf, sizeof(tidrange_buf),
                                204                 :                :                          "%d tids (%u, %u) - (%u, %u)",
                                205                 :                :                          nlist,
                                206                 :                :                          ItemPointerGetBlockNumberNoCheck(&list[0]),
                                207                 :              0 :                          ItemPointerGetOffsetNumberNoCheck(&list[0]),
                                208                 :              0 :                          ItemPointerGetBlockNumberNoCheck(&list[nlist - 1]),
                                209                 :              0 :                          ItemPointerGetOffsetNumberNoCheck(&list[nlist - 1]));
                                210                 :                :             else
                                211                 :              0 :                 snprintf(tidrange_buf, sizeof(tidrange_buf), "0 tids");
                                212                 :                : 
                                213         [ #  # ]:              0 :             if (stack->parentblk != InvalidBlockNumber)
                                214         [ #  # ]:              0 :                 elog(DEBUG3, "blk %u: parent %u highkey (%u, %u), %s",
                                215                 :                :                      stack->blkno,
                                216                 :                :                      stack->parentblk,
                                217                 :                :                      ItemPointerGetBlockNumberNoCheck(&stack->parentkey),
                                218                 :                :                      ItemPointerGetOffsetNumberNoCheck(&stack->parentkey),
                                219                 :                :                      tidrange_buf);
                                220                 :                :             else
                                221         [ #  # ]:              0 :                 elog(DEBUG3, "blk %u: root leaf, %s",
                                222                 :                :                      stack->blkno,
                                223                 :                :                      tidrange_buf);
                                224                 :                : 
                                225   [ #  #  #  # ]:              0 :             if (stack->parentblk != InvalidBlockNumber &&
                                226                 :              0 :                 ItemPointerGetOffsetNumberNoCheck(&stack->parentkey) != InvalidOffsetNumber &&
                                227   [ #  #  #  # ]:              0 :                 nlist > 0 && ItemPointerCompare(&stack->parentkey, &list[nlist - 1]) < 0)
                                228         [ #  # ]:              0 :                 ereport(ERROR,
                                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                 :              0 :             maxoff = GinPageGetOpaque(page)->maxoff;
                                244                 :              0 :             rightlink = GinPageGetOpaque(page)->rightlink;
                                245                 :                : 
                                246         [ #  # ]:              0 :             elog(DEBUG1, "page blk: %u, type data, maxoff %d", stack->blkno, maxoff);
                                247                 :                : 
                                248         [ #  # ]:              0 :             if (stack->parentblk != InvalidBlockNumber)
                                249         [ #  # ]:              0 :                 elog(DEBUG3, "blk %u: internal posting tree page with %u items, parent %u highkey (%u, %u)",
                                250                 :                :                      stack->blkno, maxoff, stack->parentblk,
                                251                 :                :                      ItemPointerGetBlockNumberNoCheck(&stack->parentkey),
                                252                 :                :                      ItemPointerGetOffsetNumberNoCheck(&stack->parentkey));
                                253                 :                :             else
                                254         [ #  # ]:              0 :                 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                 :              0 :             pd_lower = ((PageHeader) page)->pd_lower;
                                269                 :              0 :             lowersize = pd_lower - MAXALIGN(SizeOfPageHeaderData);
                                270         [ #  # ]:              0 :             if ((lowersize - MAXALIGN(sizeof(ItemPointerData))) / sizeof(PostingItem) != maxoff)
                                271         [ #  # ]:              0 :                 ereport(ERROR,
                                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                 :              0 :             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   [ #  #  #  # ]:              0 :             if (ItemPointerIsValid(&stack->parentkey) &&
                                289                 :              0 :                 rightlink != InvalidBlockNumber &&
                                290         [ #  # ]:              0 :                 !ItemPointerEquals(&stack->parentkey, &bound))
                                291         [ #  # ]:              0 :                 ereport(ERROR,
                                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))",
                                294                 :                :                                 RelationGetRelationName(rel),
                                295                 :                :                                 ItemPointerGetBlockNumberNoCheck(&bound),
                                296                 :                :                                 ItemPointerGetOffsetNumberNoCheck(&bound),
                                297                 :                :                                 stack->blkno, stack->parentblk,
                                298                 :                :                                 ItemPointerGetBlockNumberNoCheck(&stack->parentkey),
                                299                 :                :                                 ItemPointerGetOffsetNumberNoCheck(&stack->parentkey))));
                                300                 :                : 
                                301         [ #  # ]:              0 :             for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
                                302                 :                :             {
                                303                 :                :                 GinPostingTreeScanItem *ptr;
                                304                 :              0 :                 PostingItem *posting_item = GinDataPageGetPostingItem(page, i);
                                305                 :                : 
                                306                 :                :                 /* ItemPointerGetOffsetNumber expects a valid pointer */
                                307   [ #  #  #  # ]:              0 :                 if (!(i == maxoff &&
                                308                 :                :                       rightlink == InvalidBlockNumber))
                                309         [ #  # ]:              0 :                     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         [ #  # ]:              0 :                     elog(DEBUG3, "key (%u, %u) -> %u",
                                315                 :                :                          0, 0, BlockIdGetBlockNumber(&posting_item->child_blkno));
                                316                 :                : 
                                317   [ #  #  #  # ]:              0 :                 if (i == maxoff && rightlink == InvalidBlockNumber)
                                318                 :                :                 {
                                319                 :                :                     /*
                                320                 :                :                      * The rightmost item in the tree level has (0, 0) as the
                                321                 :                :                      * key
                                322                 :                :                      */
                                323   [ #  #  #  # ]:              0 :                     if (ItemPointerGetBlockNumberNoCheck(&posting_item->key) != 0 ||
                                324                 :              0 :                         ItemPointerGetOffsetNumberNoCheck(&posting_item->key) != 0)
                                325         [ #  # ]:              0 :                         ereport(ERROR,
                                326                 :                :                                 (errcode(ERRCODE_INDEX_CORRUPTED),
                                327                 :                :                                  errmsg("index \"%s\": rightmost posting tree page (blk %u) has unexpected last key (%u, %u)",
                                328                 :                :                                         RelationGetRelationName(rel),
                                329                 :                :                                         stack->blkno,
                                330                 :                :                                         ItemPointerGetBlockNumberNoCheck(&posting_item->key),
                                331                 :                :                                         ItemPointerGetOffsetNumberNoCheck(&posting_item->key))));
                                332                 :                :                 }
                                333         [ #  # ]:              0 :                 else if (i != FirstOffsetNumber)
                                334                 :                :                 {
                                335                 :              0 :                     PostingItem *previous_posting_item = GinDataPageGetPostingItem(page, i - 1);
                                336                 :                : 
                                337         [ #  # ]:              0 :                     if (ItemPointerCompare(&posting_item->key, &previous_posting_item->key) < 0)
                                338         [ #  # ]:              0 :                         ereport(ERROR,
                                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                 :                :                  */
   81                           348   [ #  #  #  #  :              0 :                 if (i == maxoff && ItemPointerIsValid(&stack->parentkey) &&
                                              #  # ]
  161                           349                 :              0 :                     ItemPointerCompare(&stack->parentkey, &posting_item->key) < 0)
                                350         [ #  # ]:              0 :                     ereport(ERROR,
                                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",
                                353                 :                :                                     RelationGetRelationName(rel),
                                354                 :                :                                     stack->blkno, i)));
                                355                 :                : 
                                356                 :                :                 /* This is an internal page, recurse into the child. */
                                357                 :              0 :                 ptr = (GinPostingTreeScanItem *) palloc(sizeof(GinPostingTreeScanItem));
                                358                 :              0 :                 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                 :                :                  */
   81                           364                 :              0 :                 ptr->parentkey = posting_item->key;
  161                           365                 :              0 :                 ptr->parentblk = stack->blkno;
                                366                 :              0 :                 ptr->blkno = BlockIdGetBlockNumber(&posting_item->child_blkno);
                                367                 :              0 :                 ptr->next = stack->next;
                                368                 :              0 :                 stack->next = ptr;
                                369                 :                :             }
                                370                 :                :         }
                                371                 :              0 :         LockBuffer(buffer, GIN_UNLOCK);
                                372                 :              0 :         ReleaseBuffer(buffer);
                                373                 :                : 
                                374                 :                :         /* Step to next item in the queue */
                                375                 :              0 :         stack_next = stack->next;
                                376                 :              0 :         pfree(stack);
                                377                 :              0 :         stack = stack_next;
                                378                 :                :     }
                                379                 :                : 
                                380                 :              0 :     MemoryContextSwitchTo(oldcontext);
                                381                 :              0 :     MemoryContextDelete(mctx);
                                382                 :              0 : }
                                383                 :                : 
                                384                 :                : /*
                                385                 :                :  * Main entry point for GIN checks.
                                386                 :                :  *
                                387                 :                :  * Allocates memory context and scans through the whole GIN graph.
                                388                 :                :  */
                                389                 :                : static void
  161 tomas.vondra@postgre      390                 :CBC          32 : gin_check_parent_keys_consistency(Relation rel,
                                391                 :                :                                   Relation heaprel,
                                392                 :                :                                   void *callback_state,
                                393                 :                :                                   bool readonly)
                                394                 :                : {
                                395                 :             32 :     BufferAccessStrategy strategy = GetAccessStrategy(BAS_BULKREAD);
                                396                 :                :     GinScanItem *stack;
                                397                 :                :     MemoryContext mctx;
                                398                 :                :     MemoryContext oldcontext;
                                399                 :                :     GinState    state;
                                400                 :                :     int         leafdepth;
                                401                 :                : 
                                402                 :             32 :     mctx = AllocSetContextCreate(CurrentMemoryContext,
                                403                 :                :                                  "amcheck consistency check context",
                                404                 :                :                                  ALLOCSET_DEFAULT_SIZES);
                                405                 :             32 :     oldcontext = MemoryContextSwitchTo(mctx);
                                406                 :             32 :     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                 :             32 :     leafdepth = -1;
                                413                 :                : 
                                414                 :                :     /* Start the scan at the root page */
                                415                 :             32 :     stack = (GinScanItem *) palloc0(sizeof(GinScanItem));
                                416                 :             32 :     stack->depth = 0;
                                417                 :             32 :     stack->parenttup = NULL;
                                418                 :             32 :     stack->parentblk = InvalidBlockNumber;
                                419                 :             32 :     stack->blkno = GIN_ROOT_BLKNO;
                                420                 :                : 
                                421         [ +  + ]:            179 :     while (stack)
                                422                 :                :     {
                                423                 :                :         GinScanItem *stack_next;
                                424                 :                :         Buffer      buffer;
                                425                 :                :         Page        page;
                                426                 :                :         OffsetNumber i,
                                427                 :                :                     maxoff,
                                428                 :                :                     prev_attnum;
                                429                 :                :         IndexTuple  prev_tuple;
                                430                 :                :         BlockNumber rightlink;
                                431                 :                : 
                                432         [ -  + ]:            152 :         CHECK_FOR_INTERRUPTS();
                                433                 :                : 
                                434                 :            152 :         buffer = ReadBufferExtended(rel, MAIN_FORKNUM, stack->blkno,
                                435                 :                :                                     RBM_NORMAL, strategy);
                                436                 :            152 :         LockBuffer(buffer, GIN_SHARE);
    8 peter@eisentraut.org      437                 :GNC         152 :         page = BufferGetPage(buffer);
  161 tomas.vondra@postgre      438                 :CBC         152 :         maxoff = PageGetMaxOffsetNumber(page);
                                439                 :            152 :         rightlink = GinPageGetOpaque(page)->rightlink;
                                440                 :                : 
                                441                 :                :         /* Do basic sanity checks on the page headers */
                                442                 :            152 :         check_index_page(rel, buffer, stack->blkno);
                                443                 :                : 
                                444         [ -  + ]:            152 :         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         [ +  + ]:            152 :         if (stack->parenttup != NULL)
                                453                 :                :         {
                                454                 :                :             GinNullCategory parent_key_category;
                                455                 :            115 :             Datum       parent_key = gintuple_get_key(&state,
                                456                 :                :                                                       stack->parenttup,
                                457                 :                :                                                       &parent_key_category);
   81                           458                 :            115 :             OffsetNumber parent_key_attnum = gintuple_get_attrnum(&state, stack->parenttup);
  161                           459                 :            115 :             ItemId      iid = PageGetItemIdCareful(rel, stack->blkno,
                                460                 :                :                                                    page, maxoff);
                                461                 :            115 :             IndexTuple  idxtuple = (IndexTuple) PageGetItem(page, iid);
   81                           462                 :            115 :             OffsetNumber page_max_key_attnum = gintuple_get_attrnum(&state, idxtuple);
                                463                 :                :             GinNullCategory page_max_key_category;
  161                           464                 :            115 :             Datum       page_max_key = gintuple_get_key(&state, idxtuple, &page_max_key_category);
                                465                 :                : 
                                466   [ +  -  -  + ]:            230 :             if (rightlink != InvalidBlockNumber &&
   81                           467                 :            115 :                 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                 :                : 
  161 tomas.vondra@postgre      474         [ #  # ]:UBC           0 :                 elog(DEBUG3, "split detected for blk: %u, parent blk: %u", stack->blkno, stack->parentblk);
                                475                 :                : 
                                476                 :              0 :                 ptr = (GinScanItem *) palloc(sizeof(GinScanItem));
                                477                 :              0 :                 ptr->depth = stack->depth;
                                478                 :              0 :                 ptr->parenttup = CopyIndexTuple(stack->parenttup);
                                479                 :              0 :                 ptr->parentblk = stack->parentblk;
                                480                 :              0 :                 ptr->blkno = rightlink;
                                481                 :              0 :                 ptr->next = stack->next;
                                482                 :              0 :                 stack->next = ptr;
                                483                 :                :             }
                                484                 :                :         }
                                485                 :                : 
                                486                 :                :         /* Check that the tree has the same height in all branches */
  161 tomas.vondra@postgre      487         [ +  + ]:CBC         152 :         if (GinPageIsLeaf(page))
                                488                 :                :         {
                                489         [ +  + ]:            146 :             if (leafdepth == -1)
                                490                 :             31 :                 leafdepth = stack->depth;
                                491         [ -  + ]:            115 :             else if (stack->depth != leafdepth)
  161 tomas.vondra@postgre      492         [ #  # ]:UBC           0 :                 ereport(ERROR,
                                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                 :                :          */
  161 tomas.vondra@postgre      502                 :CBC         152 :         prev_tuple = NULL;
                                503                 :            152 :         prev_attnum = InvalidAttrNumber;
                                504         [ +  + ]:           1744 :         for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
                                505                 :                :         {
                                506                 :           1597 :             ItemId      iid = PageGetItemIdCareful(rel, stack->blkno, page, i);
                                507                 :           1597 :             IndexTuple  idxtuple = (IndexTuple) PageGetItem(page, iid);
   81                           508                 :           1597 :             OffsetNumber current_attnum = gintuple_get_attrnum(&state, idxtuple);
                                509                 :                :             GinNullCategory current_key_category;
                                510                 :                :             Datum       current_key;
                                511                 :                : 
  161                           512         [ -  + ]:           1597 :             if (MAXALIGN(ItemIdGetLength(iid)) != MAXALIGN(IndexTupleSize(idxtuple)))
  161 tomas.vondra@postgre      513         [ #  # ]:UBC           0 :                 ereport(ERROR,
                                514                 :                :                         (errcode(ERRCODE_INDEX_CORRUPTED),
                                515                 :                :                          errmsg("index \"%s\" has inconsistent tuple sizes, block %u, offset %u",
                                516                 :                :                                 RelationGetRelationName(rel), stack->blkno, i)));
                                517                 :                : 
  161 tomas.vondra@postgre      518                 :CBC        1597 :             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                 :                :              */
   81                           529   [ +  +  +  + ]:           1597 :             if ((i != FirstOffsetNumber) &&
                                530   [ +  +  +  + ]:            151 :                 !(i == maxoff && rightlink == InvalidBlockNumber && !GinPageIsLeaf(page)))
                                531                 :                :             {
                                532                 :                :                 Datum       prev_key;
                                533                 :                :                 GinNullCategory prev_key_category;
                                534                 :                : 
  161                           535                 :           1440 :                 prev_key = gintuple_get_key(&state, prev_tuple, &prev_key_category);
   81                           536         [ +  + ]:           1440 :                 if (ginCompareAttEntries(&state, prev_attnum, prev_key,
                                537                 :                :                                          prev_key_category, current_attnum,
                                538                 :                :                                          current_key, current_key_category) >= 0)
  161                           539         [ +  - ]:              3 :                     ereport(ERROR,
                                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   [ +  +  +  + ]:           1594 :             if (stack->parenttup &&
                                550                 :                :                 i == maxoff)
                                551                 :                :             {
                                552                 :                :                 GinNullCategory parent_key_category;
   81                           553                 :            115 :                 OffsetNumber parent_key_attnum = gintuple_get_attrnum(&state, stack->parenttup);
  161                           554                 :            115 :                 Datum       parent_key = gintuple_get_key(&state,
                                555                 :                :                                                           stack->parenttup,
                                556                 :                :                                                           &parent_key_category);
                                557                 :                : 
   81                           558         [ +  + ]:            115 :                 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                 :                :                      */
  161                           569                 :              2 :                     pfree(stack->parenttup);
                                570                 :              2 :                     stack->parenttup = gin_refind_parent(rel, stack->parentblk,
                                571                 :                :                                                          stack->blkno, strategy);
                                572                 :                : 
                                573                 :                :                     /* We found it - make a final check before failing */
                                574         [ -  + ]:              2 :                     if (!stack->parenttup)
  161 tomas.vondra@postgre      575         [ #  # ]:UBC           0 :                         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                 :                :                     {
   81 tomas.vondra@postgre      579                 :CBC           2 :                         parent_key_attnum = gintuple_get_attrnum(&state, stack->parenttup);
  161                           580                 :              2 :                         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                 :                :                          */
   81                           588         [ +  - ]:              2 :                         if (ginCompareAttEntries(&state, current_attnum, current_key,
                                589                 :                :                                                  current_key_category, parent_key_attnum,
                                590                 :                :                                                  parent_key, parent_key_category) > 0)
  161                           591         [ +  - ]:              2 :                             ereport(ERROR,
                                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         [ +  + ]:           1592 :             if (!GinPageIsLeaf(page))
                                601                 :                :             {
                                602                 :                :                 GinScanItem *ptr;
                                603                 :                : 
                                604                 :            121 :                 ptr = (GinScanItem *) palloc(sizeof(GinScanItem));
                                605                 :            121 :                 ptr->depth = stack->depth + 1;
                                606                 :                :                 /* last tuple in layer has no high key */
   81                           607   [ +  +  +  - ]:            121 :                 if (i == maxoff && rightlink == InvalidBlockNumber)
  161                           608                 :              5 :                     ptr->parenttup = NULL;
                                609                 :                :                 else
   81                           610                 :            116 :                     ptr->parenttup = CopyIndexTuple(idxtuple);
  161                           611                 :            121 :                 ptr->parentblk = stack->blkno;
                                612                 :            121 :                 ptr->blkno = GinGetDownlink(idxtuple);
                                613                 :            121 :                 ptr->next = stack->next;
                                614                 :            121 :                 stack->next = ptr;
                                615                 :                :             }
                                616                 :                :             /* If this item is a pointer to a posting tree, recurse into it */
                                617         [ -  + ]:           1471 :             else if (GinIsPostingTree(idxtuple))
                                618                 :                :             {
  161 tomas.vondra@postgre      619                 :UBC           0 :                 BlockNumber rootPostingTree = GinGetPostingTree(idxtuple);
                                620                 :                : 
                                621                 :              0 :                 gin_check_posting_tree_parent_keys_consistency(rel, rootPostingTree);
                                622                 :                :             }
                                623                 :                :             else
                                624                 :                :             {
                                625                 :                :                 ItemPointer ipd;
                                626                 :                :                 int         nipd;
                                627                 :                : 
  161 tomas.vondra@postgre      628                 :CBC        1471 :                 ipd = ginReadTupleWithoutState(idxtuple, &nipd);
                                629                 :                : 
                                630         [ +  + ]:         324639 :                 for (int j = 0; j < nipd; j++)
                                631                 :                :                 {
                                632   [ +  -  +  -  :         323168 :                     if (!OffsetNumberIsValid(ItemPointerGetOffsetNumber(&ipd[j])))
                                              -  + ]
  161 tomas.vondra@postgre      633         [ #  # ]:UBC           0 :                         ereport(ERROR,
                                634                 :                :                                 (errcode(ERRCODE_INDEX_CORRUPTED),
                                635                 :                :                                  errmsg("index \"%s\": posting list contains invalid heap pointer on block %u",
                                636                 :                :                                         RelationGetRelationName(rel), stack->blkno)));
                                637                 :                :                 }
  161 tomas.vondra@postgre      638                 :CBC        1471 :                 pfree(ipd);
                                639                 :                :             }
                                640                 :                : 
                                641                 :           1592 :             prev_tuple = CopyIndexTuple(idxtuple);
   81                           642                 :           1592 :             prev_attnum = current_attnum;
                                643                 :                :         }
                                644                 :                : 
  161                           645                 :            147 :         LockBuffer(buffer, GIN_UNLOCK);
                                646                 :            147 :         ReleaseBuffer(buffer);
                                647                 :                : 
                                648                 :                :         /* Step to next item in the queue */
                                649                 :            147 :         stack_next = stack->next;
                                650         [ +  + ]:            147 :         if (stack->parenttup)
                                651                 :            113 :             pfree(stack->parenttup);
                                652                 :            147 :         pfree(stack);
                                653                 :            147 :         stack = stack_next;
                                654                 :                :     }
                                655                 :                : 
                                656                 :             27 :     MemoryContextSwitchTo(oldcontext);
                                657                 :             27 :     MemoryContextDelete(mctx);
                                658                 :             27 : }
                                659                 :                : 
                                660                 :                : /*
                                661                 :                :  * Verify that a freshly-read page looks sane.
                                662                 :                :  */
                                663                 :                : static void
                                664                 :            152 : check_index_page(Relation rel, Buffer buffer, BlockNumber blockNo)
                                665                 :                : {
                                666                 :            152 :     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         [ -  + ]:            152 :     if (PageIsNew(page))
  161 tomas.vondra@postgre      675         [ #  # ]:UBC           0 :         ereport(ERROR,
                                676                 :                :                 (errcode(ERRCODE_INDEX_CORRUPTED),
                                677                 :                :                  errmsg("index \"%s\" contains unexpected zero page at block %u",
                                678                 :                :                         RelationGetRelationName(rel),
                                679                 :                :                         BufferGetBlockNumber(buffer)),
                                680                 :                :                  errhint("Please REINDEX it.")));
                                681                 :                : 
                                682                 :                :     /*
                                683                 :                :      * Additionally check that the special area looks sane.
                                684                 :                :      */
  161 tomas.vondra@postgre      685         [ -  + ]:CBC         152 :     if (PageGetSpecialSize(page) != MAXALIGN(sizeof(GinPageOpaqueData)))
  161 tomas.vondra@postgre      686         [ #  # ]:UBC           0 :         ereport(ERROR,
                                687                 :                :                 (errcode(ERRCODE_INDEX_CORRUPTED),
                                688                 :                :                  errmsg("index \"%s\" contains corrupted page at block %u",
                                689                 :                :                         RelationGetRelationName(rel),
                                690                 :                :                         BufferGetBlockNumber(buffer)),
                                691                 :                :                  errhint("Please REINDEX it.")));
                                692                 :                : 
  161 tomas.vondra@postgre      693         [ -  + ]:CBC         152 :     if (GinPageIsDeleted(page))
                                694                 :                :     {
  161 tomas.vondra@postgre      695         [ #  # ]:UBC           0 :         if (!GinPageIsLeaf(page))
                                696         [ #  # ]:              0 :             ereport(ERROR,
                                697                 :                :                     (errcode(ERRCODE_INDEX_CORRUPTED),
                                698                 :                :                      errmsg("index \"%s\" has deleted internal page %u",
                                699                 :                :                             RelationGetRelationName(rel), blockNo)));
                                700         [ #  # ]:              0 :         if (PageGetMaxOffsetNumber(page) > InvalidOffsetNumber)
                                701         [ #  # ]:              0 :             ereport(ERROR,
                                702                 :                :                     (errcode(ERRCODE_INDEX_CORRUPTED),
                                703                 :                :                      errmsg("index \"%s\" has deleted page %u with tuples",
                                704                 :                :                             RelationGetRelationName(rel), blockNo)));
                                705                 :                :     }
  161 tomas.vondra@postgre      706         [ -  + ]:CBC         152 :     else if (PageGetMaxOffsetNumber(page) > MaxIndexTuplesPerPage)
  161 tomas.vondra@postgre      707         [ #  # ]:UBC           0 :         ereport(ERROR,
                                708                 :                :                 (errcode(ERRCODE_INDEX_CORRUPTED),
                                709                 :                :                  errmsg("index \"%s\" has page %u with exceeding count of tuples",
                                710                 :                :                         RelationGetRelationName(rel), blockNo)));
  161 tomas.vondra@postgre      711                 :CBC         152 : }
                                712                 :                : 
                                713                 :                : /*
                                714                 :                :  * Try to re-find downlink pointing to 'blkno', in 'parentblkno'.
                                715                 :                :  *
                                716                 :                :  * If found, returns a palloc'd copy of the downlink tuple. Otherwise,
                                717                 :                :  * returns NULL.
                                718                 :                :  */
                                719                 :                : static IndexTuple
                                720                 :              2 : gin_refind_parent(Relation rel, BlockNumber parentblkno,
                                721                 :                :                   BlockNumber childblkno, BufferAccessStrategy strategy)
                                722                 :                : {
                                723                 :                :     Buffer      parentbuf;
                                724                 :                :     Page        parentpage;
                                725                 :                :     OffsetNumber o,
                                726                 :                :                 parent_maxoff;
                                727                 :              2 :     IndexTuple  result = NULL;
                                728                 :                : 
                                729                 :              2 :     parentbuf = ReadBufferExtended(rel, MAIN_FORKNUM, parentblkno, RBM_NORMAL,
                                730                 :                :                                    strategy);
                                731                 :                : 
                                732                 :              2 :     LockBuffer(parentbuf, GIN_SHARE);
                                733                 :              2 :     parentpage = BufferGetPage(parentbuf);
                                734                 :                : 
                                735         [ -  + ]:              2 :     if (GinPageIsLeaf(parentpage))
                                736                 :                :     {
  161 tomas.vondra@postgre      737                 :UBC           0 :         UnlockReleaseBuffer(parentbuf);
                                738                 :              0 :         return result;
                                739                 :                :     }
                                740                 :                : 
  161 tomas.vondra@postgre      741                 :CBC           2 :     parent_maxoff = PageGetMaxOffsetNumber(parentpage);
                                742         [ +  - ]:              2 :     for (o = FirstOffsetNumber; o <= parent_maxoff; o = OffsetNumberNext(o))
                                743                 :                :     {
                                744                 :              2 :         ItemId      p_iid = PageGetItemIdCareful(rel, parentblkno, parentpage, o);
                                745                 :              2 :         IndexTuple  itup = (IndexTuple) PageGetItem(parentpage, p_iid);
                                746                 :                : 
   81                           747         [ +  - ]:              2 :         if (GinGetDownlink(itup) == childblkno)
                                748                 :                :         {
                                749                 :                :             /* Found it! Make copy and return it */
  161                           750                 :              2 :             result = CopyIndexTuple(itup);
                                751                 :              2 :             break;
                                752                 :                :         }
                                753                 :                :     }
                                754                 :                : 
                                755                 :              2 :     UnlockReleaseBuffer(parentbuf);
                                756                 :                : 
                                757                 :              2 :     return result;
                                758                 :                : }
                                759                 :                : 
                                760                 :                : static ItemId
                                761                 :           1714 : PageGetItemIdCareful(Relation rel, BlockNumber block, Page page,
                                762                 :                :                      OffsetNumber offset)
                                763                 :                : {
                                764                 :           1714 :     ItemId      itemid = PageGetItemId(page, offset);
                                765                 :                : 
                                766         [ -  + ]:           1714 :     if (ItemIdGetOffset(itemid) + ItemIdGetLength(itemid) >
                                767                 :                :         BLCKSZ - MAXALIGN(sizeof(GinPageOpaqueData)))
  161 tomas.vondra@postgre      768         [ #  # ]:UBC           0 :         ereport(ERROR,
                                769                 :                :                 (errcode(ERRCODE_INDEX_CORRUPTED),
                                770                 :                :                  errmsg("line pointer points past end of tuple space in index \"%s\"",
                                771                 :                :                         RelationGetRelationName(rel)),
                                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                 :                :      */
  161 tomas.vondra@postgre      782   [ +  -  +  - ]:CBC        1714 :     if (ItemIdIsRedirected(itemid) || !ItemIdIsUsed(itemid) ||
                                783   [ +  -  -  + ]:           1714 :         ItemIdIsDead(itemid) || ItemIdGetLength(itemid) == 0)
  161 tomas.vondra@postgre      784         [ #  # ]:UBC           0 :         ereport(ERROR,
                                785                 :                :                 (errcode(ERRCODE_INDEX_CORRUPTED),
                                786                 :                :                  errmsg("invalid line pointer storage in index \"%s\"",
                                787                 :                :                         RelationGetRelationName(rel)),
                                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                 :                : 
  161 tomas.vondra@postgre      793                 :CBC        1714 :     return itemid;
                                794                 :                : }
        

Generated by: LCOV version 2.4-beta